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

    相关文章

      网友评论

        本文标题:Runtime:常用API

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