前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Objective-C Runtime] 成员变量与属性

[Objective-C Runtime] 成员变量与属性

作者头像
Jacklin
发布2018-05-15 17:15:17
1.8K0
发布2018-05-15 17:15:17
举报
文章被收录于专栏:攻城狮的动态攻城狮的动态

在上篇文章[Objective-C Runtime] 类与对象详细讲解了Runtime机制对于类和对象相关处理,今天继续讲解一下Runtime在成员变量和属性上的处理方法和策略。

成员变量(Ivar)的数据结构

在Objective-C中,成员变量即Ivar类型,是指向结构体struct objc_ivar的指针,在Objc/runtime.h 中查到,如下所示:

代码语言:javascript
复制
typedef struct objc_ivar *Ivar;

结构体struct objc_ivar的数据结构如下所示:

代码语言:javascript
复制
struct objc_ivar {
    char *ivar_name OBJC2_UNAVAILABLE; // 变量名。
    char *ivar_type OBJC2_UNAVAILABLE; // 变量类型。
    int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移量,在对成员变量寻址时使用。

#ifdef __LP64__
    int space OBJC2_UNAVAILABLE;
#endif} 
属性的数据结构

属性(property)数据结构如下所示:

代码语言:javascript
复制
typedef struct objc_property *objc_property_t;

属性特性(Attribute)的数据结构如下所示:

代码语言:javascript
复制
typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;       
                   /**< The value of the atribute (usually empty) */
} objc_property_attribute_t;
成员变量与属性的联系
  • 本质上,一个属性一定对应一个成员变量,但是属性又不仅仅是一个成员变量,属性还会根据自己对应的属性特性的定义来对这个成员变量进行一系列的封装:提供 Getter/Setter 方法、内存管理策略、线程安全机制等等;
相关函数

Runtime 中与成员变量和属性相关的函数有很多,这里罗列出一些常用的方法:

  • Ivar class_getClassVariable(Class cls, const char *name),返回指定类的指定名字的成员变量;
  • Ivar *class_copyIvarList(Class cls, unsigned int *outCount),返回指定类的成员变量列表。调用后需要自己 free();
  • BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types),给指定的类添加成员变量。这个函数只能在objc_allocateClassPair()objc_registerClassPair() 之间调用,并且不能为一个已经存在的类添加成员变量;
  • id object_getIvar(id obj, Ivar ivar),获得对象的指定成员变量的值。速度比 object_getInstanceVariable() 快;
  • void object_setIvar(id obj, Ivar ivar, id value),设置对象指定成员变量的值。速度比object_setInstanceVariable() 快;
  • Ivar object_getInstanceVariable(id obj, const char *name, void **outValue),获取指定名字的成员变量的值;
  • Ivar object_setInstanceVariable(id obj, const char *name, void *value),设置指定名字成员变量的值;
  • const char *ivar_getName(Ivar v),获取成员变量名;
  • const char *ivar_getTypeEncoding(Ivar v),获取成员变量的类型编码;
  • ptrdiff_t ivar_getOffset(Ivar v),获取成员变量的偏移量;
  • ``objc_property_t class_getProperty(Class cls, const char *name)`, 获取指定类指定名字的属性;
  • objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount), 获取指定类的属性列表。调用后需要自己 free();
  • BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount), 给指定的类添加属性;
  • void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount),替代指定类的属性;
  • const char *property_getName(objc_property_t property),获取属性名;
  • const char *property_getAttributes(objc_property_t property),获取属性特性描述;
  • objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount),获取属性特性列表。调用后需要自己 free();
  • char *property_copyAttributeValue(objc_property_t property, const char *attributeName),获取属性特性值。调用后需要自己 free();
运行时操作成员变量和属性的示例代码
代码语言:javascript
复制
NSString * runtimePropertyGetterIMP(id self, SEL _cmd){
    Ivar ivar = class_getInstanceVariable([self class], "_runtimeProperty");    
    return object_getIvar(self, ivar);
}

void runtimePropertySetterIMP(id self, SEL _cmd, NSString *value){
    Ivar ivar = class_getInstanceVariable([self class], "_runtimeProperty");    NSString *aValue = (NSString *)object_getIvar(self, ivar);    if (![aValue isEqualToString:value]) {
        object_setIvar(self, ivar, value);
    }
}

- (void)verifyPropertyAndIvar{    

#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wundeclared-selector"
    
    //1、Add property and getter/setter method
    Class cls = objc_allocateClassPair([Animal class], "Panda", 0);    
    //add instance variable
    BOOL isSuccess = class_addIvar(cls, "_runtimeProperty", sizeof(cls), log2(sizeof(cls)), @encode(NSString));    NSLog(@"%@", isSuccess ? @"成功" : @"失败");//print 成功
    
    //add attributes
    objc_property_attribute_t type = {"T", "@\"NSString\""};
    objc_property_attribute_t ownership = {"C", ""};//C = Copy
    objc_property_attribute_t isAutomic = {"N", ""};// N = nonatomic
    objc_property_attribute_t backingVar = {"V", "_runtimeProperty"};
    objc_property_attribute_t attrubutes[] = {type, ownership, isAutomic, backingVar};
    class_addProperty(cls, "runtimeProperty", attrubutes, 4);
    class_addMethod(cls, @selector(runtimeProperty), (IMP)runtimePropertyGetterIMP, "@@:");
    class_addMethod(cls, @selector(setRuntimeProperty), (IMP)runtimePropertySetterIMP, "V@:");
    
    objc_registerClassPair(cls);    
    //2、print all properties
    unsigned int count = 0;
    objc_property_t *properties = class_copyPropertyList(cls, &count);    for (int32_t i = 0; i < count; i ++) {
        objc_property_t property = properties[i];    
    
        NSLog(@"%s, %s\n", property_getName(property), property_getAttributes(property)); 
       //print: _runtimeProperty, T@"NSString",C,N,V_runtimeProperty
    }
    free(properties); 
          
    //3、print all Ivar
    unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList(cls, &outCount);   
          
     for (int32_t i = 0; i < outCount; i ++) {
        Ivar ivar = ivars[i];       
        NSLog(@"%s, %s\n", ivar_getName(ivar), ivar_getTypeEncoding(ivar));        
      //print:_runtimeProperty, {NSString=#}
       
    }
    free(ivars); 
          
        
    //4、use property
    id panda = [[cls alloc] init];
    [panda performSelector:@selector(setRuntimeProperty) withObject:@"set-property"];    
          
    NSString *propertyValue = [panda performSelector:@selector(runtimeProperty)];   
    NSLog(@"return value = %@", propertyValue);    
    //print: return value = set-property
    
    //5、destory
    panda = nil;
    objc_disposeClassPair(cls);    
    #pragma clang diagnostic pop
    }

上述代码打印信息:

代码语言:javascript
复制
成功
runtimeProperty, T@"NSString",C,N,V_runtimeProperty
_runtimeProperty, {NSString=#}return value = set-property
  • 上面的代码中,我们在运行时动态创建了Animal 的一个子类 Panda
  • 然后为它动态添加了 Ivar:_runtimeProperty、对应的 Property:runtimeProperty、对应的 Getter/Setter方法:runtimeProperty``setRuntimeProperty
  • 接着我们遍历和打印了Panda 的 Ivar 列表和 Property 列表;
  • 然后创建了 Panda 的一个实例 panda,并使用了 Property;
  • 最后我们销毁了 pandaPanda

这里有几点需要注意的:

  • 我们不能用 class_addIvar() 函数为一个已经存在的类添加Ivar,并且 class_addIvar() 只能在 objc_allocateClassPair()objc_registerClassPair() 之间调用;
  • 添加属性特性时的各种类型字符可以参考:Property Type String(https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6)。
  • 添加一个属性及对应的成员变量后,我们还能通过 [obj valueForKey:@"propertyName"];获得属性值。

小结

本文主要讨论了Runtime中成员变量与属性相关的内容。成员变量与属性是类的数据基础,合理使用Runtime中的相关操作能使我们更加灵活地处理与类数据相关开发工作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 攻城狮的动态 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 成员变量(Ivar)的数据结构
  • 属性的数据结构
  • 成员变量与属性的联系
  • 相关函数
  • 运行时操作成员变量和属性的示例代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档