面试题答案
一键面试代码实现
假设已有类 Person
,我们通过类别为其添加属性 extraData
。
- 导入运行时头文件
在类别实现文件中,导入运行时相关头文件
<objc/runtime.h>
。
#import <objc/runtime.h>
- 定义类别
@interface Person (DynamicProperties)
@property (nonatomic, strong) NSString *extraData;
@end
- 实现类别
@implementation Person (DynamicProperties)
static const char kExtraDataKey;
- (void)setExtraData:(NSString *)extraData {
objc_setAssociatedObject(self, &kExtraDataKey, extraData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)extraData {
return objc_getAssociatedObject(self, &kExtraDataKey);
}
@end
运行时原理解释
- 关联对象机制
- 在Objective-C运行时,类别本身并不真正存储属性的实例变量。当我们为类别声明一个属性时,需要通过运行时的关联对象机制来实现属性的存储和访问。
objc_setAssociatedObject
函数用于将一个对象(这里是extraData
)与另一个对象(self
,即Person
实例)进行关联,并指定关联策略(OBJC_ASSOCIATION_RETAIN_NONATOMIC
表示强引用且非原子操作)。关联时通过一个唯一的键(这里通过static const char kExtraDataKey
定义)来标识。objc_getAssociatedObject
函数用于根据指定的键从关联对象中获取对应的对象,从而实现属性的读取。
- 方法动态解析
虽然我们在类别中声明和实现了存取方法,但运行时在处理消息发送时,会动态查找和解析方法。当向
Person
实例发送extraData
或setExtraData:
消息时,运行时会在类别中找到对应的实现并执行。这样就实现了在不修改原有类头文件和实现文件的情况下,为类动态添加属性和方法,达到灵活的代码扩展目的。