我们先来看两个版本代码 不使用@property版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @interface User : NSObject - (NSString *)name; - (void )setName:(NSString *)name; @end @implement User { NSString *_name; } - (NSString *)name { return _name; } - (void )setName:(NSString *)name { _name = [name copy ]; } @end
使用@property版本
1 2 3 4 5 6 7 8 @interface User : NSObject @property (nonatomic , copy ) NSString *name;@end @implement User @end
这两个版本的代码基本上是等价的。由此可以看出@property
默认为我们做了第一个版本中的事。接下来我们更深入了解下在runtime发生的变化。 有必要先了解两个结构体 objc_ivar
和 objc_property
objc_ivar 1 2 3 4 5 6 7 8 9 typedef objc_ivar * Ivar;struct objc_ivar { char *ivar_name; char *ivar_type; int ivar_offset; #ifdef __LP64__ int space; #endif }
获取该类现在的成员变量
1 2 3 4 5 6 7 8 9 unsigned int outCount = 0 ; Ivar * ivars = class_copyIvarList([User class ], &outCount); for (unsigned int i = 0 ; i < outCount; i ++) { Ivar ivar = ivars[i]; const char * name = ivar_getName(ivar); const char * type = ivar_getTypeEncoding(ivar); NSLog (@"类型为 %s 的 %s " ,type, name); } free(ivars);
打印结果:
runtimeIvar[602:16885] 类型为 @”NSString” 的 _name
objc_property 1 2 3 4 5 typedef struct objc_property *objc_property_t;struct objc_property { const char *name; const char *attributes; };
获取该类现在的属性及描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unsigned int outCount = 0 ; objc_property_t * properties = class_copyPropertyList([User class ], &outCount); for (unsigned int i = 0 ; i < outCount; 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 int j = 0 ; j < attrCount; j ++) { objc_property_attribute_t attr = attrs[j]; const char * name = attr.name; const char * value = attr.value; NSLog (@"属性的描述:%s 值:%s" , name, value); } free(attrs); NSLog (@"\n" ); } free(properties);
打印结果
runtimeIvar[661:27041] 属性描述为 T@”NSString”,C,N,V_name 的 name runtimeIvar[661:27041] 属性的描述:T 值:@”NSString” runtimeIvar[661:27041] 属性的描述:C 值: runtimeIvar[661:27041] 属性的描述:N 值: runtimeIvar[661:27041] 属性的描述:V 值:_name
其中T代表类型,具体参考Type Encoding ,C代表copy,N代表nonatomic,V代表属性name对应的实例变量名称,这里是_name。
在runtime里,一个类有以下三个列表:
ivar_list: 成员变量列表
method_list: 方法列表
prop_list: 属性列表
在增加一个属性时,系统会在 ivar_list
中添加成员变量描述(Ivar
),在 method_list
中添加 setter 和 getter 方法的描述,在 prop_list
中添加属性的描述(objc_property_t
)。
category 中如何使用 @property 主要要在 setter 和 getter 中用到这两个 runtime 方法: objc_setAssociatedObject
和 objc_getAssociatedObject
1 2 3 4 5 6 7 8 9 10 11 12 13 @interface MyClass (Category )@property (strong , nonatomic ) NSString *text;@end @implementation MyClass (Category )- (NSString *)text{ return objc_getAssociatedObject(self , @selector (text)); } - (void )setText:(NSString *)text{ objc_setAssociatedObject(self , @selector (text), text, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
也可以直接用宏定义
PS: Ivar详解 iOS runtime实战应用:成员变量和属性 《招聘一个靠谱的iOS》面试题参考答案 Objective-C: Property / instance variable in category