在上篇文章[Objective-C Runtime] 类与对象详细讲解了Runtime机制对于类和对象相关处理,今天继续讲解一下Runtime在成员变量和属性上的处理方法和策略。
在Objective-C中,成员变量即Ivar
类型,是指向结构体struct objc_ivar
的指针,在Objc/runtime.h 中查到,如下所示:
typedef struct objc_ivar *Ivar;
结构体struct 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}
属性(property)数据结构如下所示:
typedef struct objc_property *objc_property_t;
属性特性(Attribute)的数据结构如下所示:
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;
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_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();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
}
上述代码打印信息:
成功
runtimeProperty, T@"NSString",C,N,V_runtimeProperty
_runtimeProperty, {NSString=#}return value = set-property
Animal
的一个子类 Panda
;_runtimeProperty
、对应的 Property:runtimeProperty
、对应的 Getter/Setter
方法:runtimeProperty``setRuntimeProperty
;Panda
的 Ivar 列表和 Property 列表;Panda
的一个实例 panda
,并使用了 Property;panda
和 Panda
。这里有几点需要注意的:
class_addIvar()
函数为一个已经存在的类添加Ivar
,并且 class_addIvar()
只能在 objc_allocateClassPair()
和 objc_registerClassPair()
之间调用;[obj valueForKey:@"propertyName"];
获得属性值。小结
本文主要讨论了Runtime中成员变量与属性相关的内容。成员变量与属性是类的数据基础,合理使用Runtime中的相关操作能使我们更加灵活地处理与类数据相关开发工作。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有