美文网首页
Runtime(3)常用方法

Runtime(3)常用方法

作者: iOS资深入门 | 来源:发表于2020-06-16 16:55 被阅读0次

    一、类别中添加属性

    新建一个Person类, 添加一个name属性。

    @interface Person : NSObject
    
    @property (nonatomic) NSString * name;
    
    @end
    
    @implementation Person
    
    @end
    

    建一个Person类的类别stature,添加一个height属性。

    @interface Person (stature)
    
    @property (nonatomic) NSInteger height;
    
    @end
    
    
    @implementation Person (stature)
    
    @end
    

    然后在调用的时候发现


    image.png

    setHeight:方法未找到

        //获取property列表
        unsigned int count = 0;
        objc_property_t * list  = class_copyPropertyList([p class], &count);
    
        for (int i = 0 ; i < count; i ++) {
            objc_property_t property = list[I];
            const char * name = property_getName(property);
            NSLog(@"property - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
        }
        free(list);
    
        //获取ivar列表
        unsigned int ivarCount = 0;
        Ivar * ivars = class_copyIvarList([p class], &ivarCount);
    
        for (int i = 0; i < ivarCount; i ++) {
            Ivar ivar = ivars[I];
            const char * name = ivar_getName(ivar);
            NSLog(@"ivar - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
        }
        free(ivars);
        
        //获取方法列表
        unsigned int methodCount = 0;
        Method * methods = class_copyMethodList([p class], &methodCount);
        for (int i = 0; i < methodCount; i ++) {
            Method method = methods[I];
            SEL sel = method_getName(method);
            NSLog(@"method - %@", NSStringFromSelector(sel));
        }
        free(methods);
    

    输出

    property - height
    property - name
    property - age
    ivar - _name
    ivar - _age
    method - .cxx_destruct
    method - name
    method - setName:
    method - age
    method - setAge:
    

    发现只是添加了property,并没有自动生成成员变量和set、get方法。
    手动添加setter/getter方法,使用runtime关联属性后即可正常使用。

    - (void)setHeight:(NSInteger)height {
        const char * key = "height";
        objc_setAssociatedObject(self, key, @(height), OBJC_ASSOCIATION_ASSIGN);
    }
    
    - (NSInteger)height {
        const char * key = "height";
        return [objc_getAssociatedObject(self, key) integerValue];
    }
    

    二、动态添加类

        const char * className = "MyClass";
        Class MyClass = objc_allocateClassPair([NSObject class], className, 0);
        //在objc_allocateClassPair  和 objc_registerClassPair之间添加变量,不然会添加失败。
        objc_registerClassPair(MyClass);
    
    官方文档描述

    三、添加实例变量

        const char * key = "name";
        BOOL isSuccess = class_addIvar(MyClass, key, sizeof(NSString *), 0, "@");
        NSLog(@"name添加%@", isSuccess ? @"成功":@"失败");
        objc_registerClassPair(MyClass);
    
        const char * ageKey = "age";
        isSuccess = class_addIvar(MyClass, ageKey, sizeof(unsigned int), 0, "I");
        NSLog(@"age添加%@", isSuccess ? @"成功":@"失败");
    
    

    输出

    name添加成功
    age添加失败
    

    原因

    1.因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,同时runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。
    2.运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
    https://www.jianshu.com/p/faf14147c25d

        //变量存取
        id myclass = [[MyClass alloc] init];
        Ivar nameIvar = class_getInstanceVariable(MyClass, key);
        object_setIvar(myclass, nameIvar, @"小强");
    
        NSLog(@"%@", object_getIvar(myclass, nameIvar));
    

    四、动态添加属性

    Property Type String
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6

    const char * propertyDicKey = "_dictForCustProperty";
    
    {
        //存储添加的属性与值
        class_addIvar(MyClass, propertyDicKey, sizeof([NSMutableDictionary class]), 0, "@");
    
        id myClass = [[MyClass alloc] init];
        //创建实例之后初始化一下
        NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
        Ivar propertyIvar = class_getInstanceVariable(MyClass, propertyDicKey);
        object_setIvar(myClass, propertyIvar, dic);
    
    }
    
    - (void)addPropertyToClass:(Class)class propertyName:(NSString *)propertyName typeClass:(Class)typeClass {
        objc_property_attribute_t type = {"T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass(typeClass)] UTF8String]};
        //C copy 参考Property Type String
        objc_property_attribute_t ownership = {"C", ""};
        objc_property_attribute_t backingivar = {"V", "_name"};
        objc_property_attribute_t attrs[] = {type, ownership, backingivar};
        
        //添加属性
        class_addProperty(class, [propertyName UTF8String], attrs, 3);
        
        //添加 setter / getter
        SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
        class_addMethod(class, setterSel, (IMP)setValue, "@@:");
        class_addMethod(class, NSSelectorFromString(propertyName), (IMP)getter, "v@:");
        
    }
    
    void setValue (id self, SEL _cmd, id value) {
        //sel转为key  setName: --> name
        NSString * key = [NSStringFromSelector(_cmd) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""];
        NSString * head = [[key substringWithRange:NSMakeRange(0, 1)] lowercaseString];
        key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head];
        key = [key substringToIndex:key.length - 1];
        
        
        Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
        NSMutableDictionary * dic = object_getIvar(self, ivar);
        [dic setObject:value forKey:key];
        object_setIvar(self, ivar, dic);
    }
    
    id getter (id self, SEL _cmd) {
        
        Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
        NSMutableDictionary * dic = object_getIvar(self, ivar);
    
        return [dic objectForKey:NSStringFromSelector(_cmd)];
    }
    

    五、动态添加方法

    {
        //添加实例方法
        class_addMethod(MyClass, @selector(methodTest), (IMP)methodTestIMP, "v@:");
        objc_msgSend(myclass, @selector(methodTest));
    
        //添加类方法  
        class_addMethod(object_getClass(MyClass), @selector(classMethodTest), (IMP)classMethodTestIMP, "v@:");
        objc_msgSend(MyClass, @selector(classMethodTest));
    }
    
    void methodTestIMP (id self, SEL _cmd) {
        NSLog(@"这是一个实例方法");
    };
    
    void classMethodTestIMP (id self, SEL _cmd) {
        NSLog(@"这是一个类方法");
    };
    
    

    type encodings
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100

    六、方法交换

    + (void)load {
        Method old = class_getInstanceMethod([self class], @selector(viewWillAppear:));
        Method new = class_getInstanceMethod([self class], @selector(track_viewWillAppear:));
        method_exchangeImplementations(old, new);
    }
    
    - (void)track_viewWillAppear:(BOOL)animated {
        NSLog(@"%@---%@",self , NSStringFromSelector(_cmd));
        [self track_viewWillAppear:animated];
    }
    

    相关文章

      网友评论

          本文标题:Runtime(3)常用方法

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