如何为单个Objective-C对象实例动态替换只读属性getter实现?
实现方案
要给单个FooClass实例替换foo属性的getter实现,核心是利用Objective-C的动态运行时特性,通过为目标实例动态切换isa到临时子类的方式实现,且不会影响类的其他实例。
核心思路
- 动态创建一个继承自
FooClass的临时子类,仅用于承载自定义的getter逻辑 - 将目标实例的
isa指向这个临时子类,而非修改原类的方法实现 - 通过关联对象为每个实例绑定独立的自定义逻辑block,临时子类的getter会从当前实例中取出block执行
代码实现
1. 定义类型与关联Key
// 定义对应foo getter的block类型 typedef id (^FooGetterBlock)(void); // 用于关联block的唯一Key static char kFooGetterBlockKey;
2. 实现替换逻辑函数
void overrideFooGetterForFooClassInstance(FooClass *instance, FooGetterBlock block) { if (!instance || !block) return; Class originalClass = object_getClass(instance); NSString *tempClassName = [NSString stringWithFormat:@"%@_InstanceOverride", NSStringFromClass(originalClass)]; Class tempClass = NSClassFromString(tempClassName); // 确保临时类只创建一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 动态创建临时子类 tempClass = objc_allocateClassPair(originalClass, tempClassName.UTF8String, 0); if (!tempClass) return; // 获取原类的foo getter方法 Method originalGetter = class_getInstanceMethod(originalClass, @selector(foo)); if (!originalGetter) return; // 实现自定义的getter逻辑:优先执行关联的block,否则回退到原实现 IMP customIMP = imp_implementationWithBlock(^(id self) { FooGetterBlock getterBlock = objc_getAssociatedObject(self, &kFooGetterBlockKey); if (getterBlock) { return getterBlock(); } // 回退到原类的getter实现 return ((id (*)(id, SEL))method_getImplementation(originalGetter))(self, _cmd); }); // 为临时类添加自定义getter方法 const char *methodTypes = method_getTypeEncoding(originalGetter); class_addMethod(tempClass, @selector(foo), customIMP, methodTypes); // 注册临时类到运行时 objc_registerClassPair(tempClass); }); // 将目标实例的isa切换到临时子类 object_setClass(instance, tempClass); // 将自定义block关联到实例 objc_setAssociatedObject(instance, &kFooGetterBlockKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC); }
使用示例
// 创建两个FooClass实例 FooClass *instanceA = [[FooClass alloc] init]; FooClass *instanceB = [[FooClass alloc] init]; // 仅为instanceA替换foo的getter逻辑 overrideFooGetterForFooClassInstance(instanceA, ^id{ return @"自定义返回值"; }); // 验证效果 NSLog(@"instanceA.foo = %@", instanceA.foo); // 输出:自定义返回值 NSLog(@"instanceB.foo = %@", instanceB.foo); // 输出:nil(原默认实现)
注意事项
- 临时类复用:临时类仅会创建一次,所有需要替换getter的
FooClass实例都会共用这个类,避免重复创建类带来的性能开销 - 内存管理:使用
OBJC_ASSOCIATION_COPY_NONATOMIC关联block,确保block的生命周期与实例一致,同时避免循环引用(若block中引用实例,需用weak修饰) - 兼容性:自定义getter会回退到原类实现,若原类的getter逻辑有更新,不影响替换后的实例
- 线程安全:通过
dispatch_once保证临时类的创建是线程安全的,避免多线程下重复创建类的问题
内容的提问来源于stack exchange,提问作者Isaaс Weisberg
相关产品推荐
相关产品推荐

