Objective-C Runtime: 变量和属性

作者: 繁华落尽丶lee | 来源:发表于2016-08-24 12:05 被阅读99次

    本文只是整理Runtime中,成员变量、属性、关联对象、方法交换使用实例。不会很细致的讲解Runtime的内容,如果想了解Runtime更多内容,可以移步这里,查看大神们关于Runtime博文。

    成员变量和属性

    成员变量(Ivar)

    定义

    1、Ivar:实例变量类型, 其实是一个指向objc_ivar结构体的指针

    typedef struct objc_ivar *Ivar;
    
    操作方法
    // 获取成员变量名字
    const char * ivar_getName(Ivar v);
    // 获取成员变量类型编码
    const char * ivar_getTypeEncoding(Ivar v);
    //获取成员变量的偏移量
    ptrdiff_t ivar_getOffset(Ivar v);
    //注:对于id类型或其他类型对象的实例变量,可以调用object_getIvar和object_setIvar直接访问成员变量,而不使用偏移量。
    
    使用实例

    User.h

    @interface User : NSObject {
        NSString *_name;
    }
    @property NSString *sex;
    @property (nonatomic, copy) NSDictionary *dict;
    @property (nonatomic, assign) NSInteger age;
    @property (nonatomic, assign) double tall;
    @property (nonatomic, assign) NSUInteger height;
    - (void)getIvar;
    - (void)getProperty;
    @end
    
    

    User.m

    - (void)getIvar{
        unsigned int count = 0;
        //获取所有的成员变量
        Ivar *ivars = class_copyIvarList([User class], &count);
         for (unsigned i = 0; i < count;  i ++) {
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar); //成员变量名称
            const char *type = ivar_getTypeEncoding(ivar); //成员变量类型
            NSLog(@"类型 %s 的 %s", type, name);
        }
        free(ivars); //手动释放
    }
    

    注: class_copyIvarList()方法,请移步在另一篇文章:Objective-C Runtime:类和对象

    输出结果:

    LearnRuntime[836:9888] 类型 @"NSString" 的 _name
    LearnRuntime[836:9888] 类型 @"NSString" 的 _sex
    LearnRuntime[836:9888] 类型 @"NSDictionary" 的 _dict
    LearnRuntime[836:9888] 类型 q 的 _age
    LearnRuntime[836:9888] 类型 d 的 _tall
    LearnRuntime[836:9888] 类型 Q 的 _height
    

    注:关于输出结果为什么是_name, 请移步《招聘一个靠谱的 iOS》—参考答案(上)- 14题

    属性(property)

    定义

    1、objc_property_t:是表示Objective-C声明的属性的类型,其实是指向objc_property结构体的指针。

    typedef struct objc_property *objc_property_t;
    

    2、objc_property_attribute_t: 定义了属性的特性,结构体如下:

    typedef struct {
        const char *name;           /**< The name of the attribute */
        const char *value;          /**< The value of the attribute (usually empty) */
    } objc_property_attribute_t;
    
    操作方法
    // 获取属性名
    const char * property_getName(objc_property_t property);
    // 获取属性特性描述字符串
    const char * property_getAttributes(objc_property_t property);
    // 获取属性中指定的特性
    char * property_copyAttributeValue ( objc_property_t property, const char *attributeName );
    // 获取属性的特性列表
    objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount );
    

    注:property_copyAttributeValue和property_copyAttributeList, 返回值在使用后需要手动释放free();

    使用实例

    User.m 添加

    - (void)getProperty {
        unsigned int count = 0;
        // 获取属性列表
        objc_property_t * properties = class_copyPropertyList([User class], &count);
         for (unsigned i = 0; i < count;  i ++) {
            objc_property_t property = properties[i];
            const char *name = property_getName(property);
            const char *propertyAttr = property_getAttributes(property);
            NSLog(@"属性描述为%s的%s", propertyAttr, name);
            unsigned int  attrCount = 0;
            //属性的特性列表
            objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
            for (unsigned i = 0; i < attrCount; i ++) {
                objc_property_attribute_t attr = attrs[i];
                const char *name = attr.name;
                const char *value = attr.value;
                NSLog(@"属性的特性描述:%s值:%s", name, value);
            }
            free(attrs);
            NSLog(@"\n");
        }
        free(properties);
    }
    

    输出结果

    LearnRuntime[1190:51044] 属性描述为T@"NSString",&,V_sex的sex
    LearnRuntime[1190:51044] 属性的特性描述:T值:@"NSString"
    LearnRuntime[1190:51044] 属性的特性描述:&值:
    LearnRuntime[1190:51044] 属性的特性描述:V值:_sex
    LearnRuntime[1190:51044] 
    LearnRuntime[1190:51044] 属性描述为T@"NSDictionary",C,N,V_dict的dict
    LearnRuntime[1190:51044] 属性的特性描述:T值:@"NSDictionary"
    LearnRuntime[1190:51044] 属性的特性描述:C值:
    LearnRuntime[1190:51044] 属性的特性描述:N值:
    LearnRuntime[1190:51044] 属性的特性描述:V值:_dict
    LearnRuntime[1190:51044] 
    LearnRuntime[1190:51044] 属性描述为Tq,N,V_age的age
    LearnRuntime[1190:51044] 属性的特性描述:T值:q
    LearnRuntime[1190:51044] 属性的特性描述:N值:
    LearnRuntime[1190:51044] 属性的特性描述:V值:_age
    LearnRuntime[1190:51044] 
    LearnRuntime[1190:51044] 属性描述为Td,N,V_tall的tall
    LearnRuntime[1190:51044] 属性的特性描述:T值:d
    LearnRuntime[1190:51044] 属性的特性描述:N值:
    LearnRuntime[1190:51044] 属性的特性描述:V值:_tall
    LearnRuntime[1190:51044] 
    LearnRuntime[1190:51044] 属性描述为TQ,N,V_height的height
    LearnRuntime[1190:51044] 属性的特性描述:T值:Q
    LearnRuntime[1190:51044] 属性的特性描述:N值:
    LearnRuntime[1190:51044] 属性的特性描述:V值:_height
    

    注:objc_property_getAttribute_t结构体包含name和value,属性如下:

    属性类型  name值:T                                       value:变化
    编码类型  name值:C(copy) &(strong) W(weak) 空(assign) 等  value:无
    非/原子性 name值:空(atomic) N(Nonatomic)                    value:无
    变量名称  name值:V                                        value:变化
    

    关联对象(Associated objects)

    定义

    Associated objects是Objective-C 2.0运行时一个特性。<objc/runtime.h>中定义三个允许将任何键值在运行时关联到对象上的函数:

    objc_setAssociatedObject
    objc_getAssociatedObject
    objc_removeAssociatedObjects
    

    使用三个函数,开发者可以对已经存在的类扩展中添加自定义的属性

    删除属性

    如果你尝试使用objc_removeAssociatedObjects()进行删除操作,但官方文档告诉我们不应该手动调用这个函数,通常使用objc_setAssocatedObject方法传入nil值清除关联。关于AssociatedObjects相关知识,请看NShipster上的文章Associated Objects

    使用实例

    NSObject+AssociatedObject.h

    @interface NSObject (AssociatedObject)
    @property (nonatomic, strong) id associatedObject;
    @end
    

    NSObject+AssociatedObject.m

    @implementation NSObject (AssociatedObject)
    @dynamic associatedObject;
    - (void)setAssociatedObject:(id)object {
         objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (id)associatedObject {
        return objc_getAssociatedObject(self, @selector(associatedObject));
    }
    

    方法交换(Method swizzling)

    定义

    Method Swizzling是改变selector的实际实现的技术,通过它可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。

    操作方法

    //获取实例方法
    class_getInstanceMethod
    //获取方法的实现
    method_getImplementation
    //获取实现的编码类型
    method_getTypeEncoding
    //给方法添加实现
    class_addMethod
    //用一个方法的实现替换另一个方法的实
    class_replaceMethod
    //交换两个方法的实现
    method_exchangeImplementations
    

    使用实例

    将UIViewController中的ViewWillAppear:方法替换成自定义方法

    #import <objc/runtime.h>
    @implementation UIViewController (tracking)
    
    /**
     *  理解: [self class] 和 object_getClass(self)区别和联系?
     */
     
    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        
            Class class = [self class]; //注意:如果是类方法,则需要 Class class = object_getClass((id)self);
            
            SEL originalSelector = @selector(viewWillAppear:);
            SEL swizzledSelector = @selector(xxx_viewWillAppear:);
            
            Method originalMethod = class_getInstanceMethod(class, originalSelector);
            Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
            //注意: 在交换方法实现,需要判断原有方法实现是否存在,存在才能交换
            // 如何判断?添加原有方法,如果成功,表示原有方法不存在,失败,表示原有方法存在
            // 原有方法可能没有实现,所以这里添加方法实现,用自己方法实现
            // 这样做的好处:方法不存在,直接把自己方法的实现作为原有方法的实现,调用原有方法,就会来到当前方法的实现
            BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
            if (didAddMethod) {
                class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod);
            }
            
        });
    }
    /**
     *  添加自定义的方法
     *
     *  @param animated 
     */
    - (void)xxx_viewWillAppear: (BOOL) animated {
        [self xxx_viewWillAppear:animated]; //这里不会造成死循环,为什么呢?
        NSLog(@"viewWillAppear: %@", self);
    }
    @end
    

    使用method swizzling 修改UIViewController的@selector(viewWillAppear:)对应的函数指针,指向自定义的xxx_viewWillAppear:的实现。具体讲解请看Method Swizzling

    小结

    文章作为学习笔记,目的是方便以后查看。另外,本人能力有限,如有错误欢迎指正。

    参考资料

    这里整理关于Runtime经典博文。

    相关文章

      网友评论

        本文标题:Objective-C Runtime: 变量和属性

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