runtime-对成员变量和属性的操作
- 成员变量
首先我们来看看成员变量在runtime中是什么样的
在runtime中成员变量是一个objc_ivar类型的结构体,结构体定义如下
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 }
我们利用runtime对成员变量进行操作主要有以下几种:
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) //获取所有成员变量 const char *ivar_getName(Ivar v) //获取某个成员变量的名字 const char *ivar_getTypeEncoding(Ivar v) //获取某个成员变量的类型编码 Ivar class_getInstanceVariable(Class cls, const char *name) //获取某个类中指定名称的成员变量 id object_getIvar(id obj, Ivar ivar) //获取某个对象中的某个成员变量的值 void object_setIvar(id obj, Ivar ivar, id value) //设置某个对象的某个成员变量的值
接下来上代码来使用一下这些函数,首先我们在.h文件中定义一个Person类,并声明一个personGetMessage方法和自定义类方法
personGetMessage方法是打印出对象的信息,自定义类方法就是创建对象并给对象赋值
{ NSString *clan; } @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, strong) NSNumber *height; @property (nonatomic, copy) NSString *gender; - (void)personGetMessage;// 打印出对象的信息
// 创建对象并给对象赋值 + (Person *)personWithClan:(NSString *)clan name:(NSString *)name age:(NSInteger)age height:(NSNumber *)height gender:(NSString *)gender;
然后是在.m文件中的实现
- (void)personGetMessage { unsigned int count; // 参数一:类名 参数二:成员变量的数量 Ivar *ivarList = class_copyIvarList([self class], &count); for (int i = 0; i < count; i ++) { Ivar ivar = ivarList[i];
// 输出成员变量的名字,类型编码和值 NSLog(@"name = %s, typeEncoding = %s, value = %@",ivar_getName(ivar), ivar_getTypeEncoding(ivar), object_getIvar(self, ivar)); } } + (Person *)personWithClan:(NSString *)clan name:(NSString *)name age:(NSInteger)age height:(NSNumber *)height gender:(NSString *)gender { Person *person = [[Person alloc] init]; unsigned int count; Ivar *ivarList = class_copyIvarList(self, &count);// 获取所有成员变量 /** * 设置某个对象的某个成员变量的值 * * @param obj#> 要设置的对象 description#> * @param ivar#> 对象的成员变量 description#> * @param value#> 成员变量的值 description#> */ object_setIvar(person, ivarList[0], clan); object_setIvar(person, ivarList[1], name); object_setIvar(person, ivarList[2], @(age)); object_setIvar(person, ivarList[3], height); Ivar ivar = class_getInstanceVariable(self, "_gender"); object_setIvar(person, ivar, gender); return person; }
在程序的入口创建Person对象并且调用presonGetMessage方法控制台就会打印出我们创建的person对象的信息
Person *person = [Person personWithClan:@"汉族" name:@"zhangsan" age:21 height:@66 gender:@"male"]; [person personGetMessage];
以上两个方法的实现基本上把成员变量的基本操作函数都使用了一遍,接下来我们看看属性在runtime中是什么样的
- 属性
属性在runtime中的定义如下
/// An opaque type that represents an Objective-C declared property. typedef struct objc_property *objc_property_t; /// Defines a property attribute typedef struct { const char *name; /**< The name of the attribute */ const char *value; /**< The value of the attribute (usually empty) */ } objc_property_attribute_t;
属性的本质是一个指向objc_property的结构体指针。跟成员变量一样,runtime中一样为属性定义了一系列对属性的操作函数:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) //获取所有属性的列表 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) //获取所有属性的特性
接下来我们来使用一下上述这些方法,依然还是在Person类中操作
在.h文件中声明一个getAttributeOfproperty的实例方法并在.m文件中实现这个方法
- (void)getAttributeOfproperty { unsigned int outCount; objc_property_t *propertyList = class_copyPropertyList([Person class], &outCount); for (NSInteger i = 0; i < outCount; i ++) { NSLog(@"属性:%s,它的特性描述:%s",property_getName(propertyList[i]),property_getAttributes(propertyList[i]));// 获取属性列表只会获取有property属性声明的变量 } }
因为获取属性列表只会获取有property属性声明的变量,所以成员变量clan就获取不到了,以下是这个方法的打印结果