Runtime:常用API

作者: 码小菜 | 来源:发表于2020-03-04 17:35 被阅读0次
夜景

目录
一,类
二,成员变量
三,属性
四,方法
五,交换函数

一,类

1,创建和销毁

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 创建类
        Class newClass = objc_allocateClassPair([NSObject class], "Person", 0);
        // 注册类
        objc_registerClassPair(newClass);
        id person = [newClass new];
        NSLog(@"%@", person);
        // 销毁类
        objc_disposeClassPair(newClass);
    }
    return 0;
}

// 打印
<Person: 0x10066a270>

2,获取和设置

// Person
@interface Person : NSObject
- (void)eat;
+ (void)run;
@end

@implementation Person
- (void)eat {
    NSLog(@"%s", __func__);
}
+ (void)run {
    NSLog(@"%s", __func__);
}
@end

// Dog
@interface Dog: NSObject
- (void)eat;
+ (void)run;
@end

@implementation Dog
- (void)eat {
    NSLog(@"%s", __func__);
}
+ (void)run {
    NSLog(@"%s", __func__);
}
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 获取isa指向的Class(传实例对象返回类对象,传类对象返回元类对象)
        NSLog(@"%@", object_getClass([Person new]));
        // 设置isa指向的Class(设置实例对象的类对象,设置类对象的元类对象)
        Person *person = [Person new];
        object_setClass(person, [Dog class]);
        object_setClass([Person class], object_getClass([Dog class]));
        [person eat];
        [Person run];
        // 是否为类对象
        NSLog(@"%d", object_isClass(object_getClass([Person new])));
        // 是否为元类对象
        NSLog(@"%d", class_isMetaClass(object_getClass([Person class])));
    }
    return 0;
}

// 打印
Person
-[Dog eat]
+[Dog run]
1
1
二,成员变量

1,获取和设置

// Person
@interface Person : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@end

@implementation Person
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 获取成员变量
        Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
        // 获取成员变量的名称和类型
        NSLog(@"%s---%s", ivar_getName(nameIvar), ivar_getTypeEncoding(nameIvar));
        // 获取和设置成员变量的值
        Person *person = [Person new];
        object_setIvar(person, nameIvar, @"111");
        NSLog(@"%@", object_getIvar(person, nameIvar));
    }
    return 0;
}

// 打印
_name---@"NSString"
111

2,添加

  • 代码
int main(int argc, char * argv[]) {
    @autoreleasepool {
        Class newClass = objc_allocateClassPair([NSObject class], "Person", 0);
        // 添加成员变量
        class_addIvar(newClass, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        objc_registerClassPair(newClass);
        id person = [newClass new];
        [person setValue:@"111" forKey:@"_name"];
        NSLog(@"%@", [person valueForKey:@"_name"]);
    }
    return 0;
}

// 打印
111
  • 说明

1>已经存在的类不能添加成员变量
2>动态创建的类必须在注册之前添加成员变量
3>已经存在或注册的类,它的结构已经确定,成员变量列表是只读的,不可以修改

// ro是readonly的缩写
struct class_ro_t {
   const ivar_list_t *ivars;
};

3,列表

  • 基本使用
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 获取成员变量列表
        unsigned int count;
        Ivar *ivarList = class_copyIvarList([Person class], &count);
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivarList[i];
            NSLog(@"%s---%s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
        }
        free(ivarList);
    }
    return 0;
}

// 打印
_age---i
_name---@"NSString"
  • 应用(字典转模型)
// NSObject
@interface NSObject (Add)
+ (instancetype)yj_modelWithDict:(NSDictionary *)dict;
@end

@implementation NSObject (Add)
+ (instancetype)yj_modelWithDict:(NSDictionary *)dict {
    id model = [self new];
    
    unsigned int count;
    Ivar *ivarList = class_copyIvarList(self, &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 去掉下划线
        ivarName = [ivarName substringFromIndex:1];
        [model setValue:dict[ivarName] forKey:ivarName];
    }
    free(ivarList);
    
    return model;
}
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSDictionary *dict = @{@"age" : @1, @"name" : @"111"};
        Person *person = [Person yj_modelWithDict:dict];
        NSLog(@"%d---%@", person.age, person.name);
    }
    return 0;
}

// 打印
1---111
三,属性

1,获取

// Person
@interface Person : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@end

@implementation Person
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 获取属性
        objc_property_t nameProperty = class_getProperty([Person class], "name");
        // 获取属性的名称和其他信息
        NSLog(@"%s---%s", property_getName(nameProperty), property_getAttributes(nameProperty));
        // 获取属性的其他信息列表
        unsigned int count;
        objc_property_attribute_t *attributeList = property_copyAttributeList(nameProperty, &count);
        for (int i = 0; i < count; i++) {
            objc_property_attribute_t attribute = attributeList[i];
            NSLog(@"%s---%s", attribute.name, attribute.value);
        }
        free(attributeList);
    }
    return 0;
}

// 打印
name---T@"NSString",C,N,V_name
T---@"NSString" // type(类型):@表示对象,"NSString"表示具体类型
C---            // copy
N---            // nonatomic
V---_name       // variable(成员变量)

2,添加和列表

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 添加属性
        objc_property_attribute_t attribute1 = {"T", "@\"NSNumber\""};
        objc_property_attribute_t attribute2 = {"&", ""}; // strong
        objc_property_attribute_t attribute3 = {"N", ""};
        objc_property_attribute_t attribute4 = {"V", "_height"};
        objc_property_attribute_t attributeList[] = {attribute1, attribute2, attribute3, attribute4};
        class_addProperty([Person class], "height", attributeList, 4);
        // 获取属性列表
        unsigned int count;
        objc_property_t *propertyList = class_copyPropertyList([Person class], &count);
        for (int i = 0; i < count; i++) {
            objc_property_t property = propertyList[i];
            NSLog(@"%s---%s", property_getName(property), property_getAttributes(property));
        }
        free(propertyList);
    }
    return 0;
}

// 打印
height---T@"NSNumber",&,N,V_height
age---Ti,N,V_age
name---T@"NSString",C,N,V_name
四,方法

1,获取

// Person
@interface Person : NSObject
- (int)eat:(int)food;
+ (void)run;
@end

@implementation Person
- (int)eat:(int)food {
    NSLog(@"%s", __func__);
    return 1;
}
+ (void)run {
    NSLog(@"%s", __func__);
}
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 获取实例方法
        Method eatMethod = class_getInstanceMethod([Person class], @selector(eat:));
        // 获取类方法
        Method runMethod = class_getClassMethod(object_getClass([Person class]), @selector(run));
        // 获取方法的名称
        NSLog(@"%@", NSStringFromSelector(method_getName(eatMethod)));
        // 获取方法的返回值类型
        NSLog(@"%s", method_copyReturnType(eatMethod));
        // 获取方法的参数个数(系统默认会添加两个参数)
        NSLog(@"%d", method_getNumberOfArguments(eatMethod));
        // 获取方法第三个参数的类型
        NSLog(@"%s", method_copyArgumentType(eatMethod, 2)); 
        // 获取方法返回值类型和参数类型的编码
        NSLog(@"%s", method_getTypeEncoding(eatMethod));
    }
    return 0;
}

// 打印
eat:
i
3 
i
i20@0:8i16

2,SELIMP

void run2() {
    NSLog(@"begin run2");
}

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 用字符串生成SEL
        SEL runSel = sel_registerName("run");
        
        Method runMethod = class_getInstanceMethod(object_getClass([Person class]), @selector(run));
        // 获取方法的IMP
        IMP runImp = method_getImplementation(runMethod);
        // 重设方法的IMP(函数)
        method_setImplementation(runMethod, (IMP)run2);
        [Person run];
        // 重设方法的IMP(block)
        void(^impBlock)(void) = ^{
            NSLog(@"imp with block");
        };
        method_setImplementation(runMethod, imp_implementationWithBlock(impBlock));
        [Person run];
    }
    return 0;
}

// 打印
begin run2
imp with block

3,添加和列表

void work() {
    NSLog(@"begin work");
}

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 添加方法
        class_addMethod([Person class], sel_registerName("work"), (IMP)work, "v");
        [[Person new] performSelector:@selector(work)];
        // 获取方法列表
        unsigned int count;
        Method *methodList = class_copyMethodList([Person class], &count);
        for (int i = 0; i < count; i++) {
            Method method = methodList[i];
            NSLog(@"%@", NSStringFromSelector(method_getName(method)));
        }
        free(methodList);
    }
    return 0;
}

// 打印
begin work
work
eat:
五,交换函数

1,基本使用

// Person
@interface Person : NSObject
- (void)eat;
- (void)run;
@end

@implementation Person
- (void)eat {
    NSLog(@"%s", __func__);
}
- (void)run {
    NSLog(@"%s", __func__);
}
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        // 交换方法的IMP
        Method eatMethod = class_getInstanceMethod([Person class], @selector(eat));
        Method runMethod = class_getInstanceMethod([Person class], @selector(run));
        method_exchangeImplementations(eatMethod, runMethod);
        Person *person = [Person new];
        [person eat];
        [person run];
    }
    return 0;
}

// 打印
-[Person run]
-[Person eat]

2,应用(防止数组下标越界)

// NSArray
@interface NSArray (Add)
@end

@implementation NSArray (Add)
+ (void)load {
    // NSArray的真实类型是__NSArrayI
    Method systemMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndexedSubscript:));
    Method customMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(yj_objectAtIndexedSubscript:));
    method_exchangeImplementations(systemMethod, customMethod);
}
// 系统调用的并不是objectAtIndex:
- (id)yj_objectAtIndexedSubscript:(NSUInteger)idx {
    if (idx < self.count) {
        return [self yj_objectAtIndexedSubscript:idx];
    } else {
        return @"下标已越界";
    }
}
@end

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSArray *array = @[@"1", @"2", @"3"];
        NSLog(@"%@", array[3]);
    }
    return 0;
}

// 打印(交换前)
*** Terminating app due to uncaught exception 'NSRangeException', 
reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 2]'

// 打印(交换后)
下标已越界

3,本质

void method_exchangeImplementations(Method m1, Method m2)
{
    if (!m1  ||  !m2) return;

    mutex_locker_t lock(runtimeLock);

    // 交换IMP
    IMP m1_imp = m1->imp;
    m1->imp = m2->imp;
    m2->imp = m1_imp;

    // 清理方法缓存
    flushCaches(nil);

    updateCustomRR_AWZ(nil, m1);
    updateCustomRR_AWZ(nil, m2);
}
  • 图解
交换IMP

相关文章

  • runtime02-常用API

    runtime常用API runtime API01-类相关 runtime API01-类相关-事例01 run...

  • 基础篇

    Runtime之必备C知识 Runtime之类的本质 Runtime之消息处理策略 Runtime之常用API 进...

  • Runtime 相关 API

    以下列举了使用 Runtime 时常用到的 部分API ,并非全部 Runtime 类 相关 API动态创建一个类...

  • 11.runtime 理解 和 常用点

    问题 1.runtime 个人理解2.runtime 常用api3.runtime 日常运用解决问题4.runti...

  • 常用Runtime API

    前言:本文只是分类列举一些常用Runtime API?一些Runtime 常用场景 1. 类 动态创建一个类 注册...

  • 常用 Runtime API

    记录一下常用的Runtime API 类: 成员变量: 属性: 方法:

  • Runtime:常用API

    目录一,类二,成员变量三,属性四,方法五,交换函数 一,类 1,创建和销毁 2,获取和设置 二,成员变量 1,获取...

  • Runtime常用API

    Runtime API demo 类相关 动态创建一个类(参数:父类,类名,额为的内存空间) 注册一个类(成员变量...

  • Runtime常用API

    类相关 获取isa指向的Class 方法实现:Class object_getClass(id obj) { i...

  • iOS底层原理 - Runtime-03

    Runtime API - 类 Runtime API – 成员变量 Runtime API – 属性 Runt...

网友评论

    本文标题:Runtime:常用API

    本文链接:https://www.haomeiwen.com/subject/quwtkhtx.html