具有Objective C的动态Getter和Setter
我处于这样一种情况,我想在运行时动态生成类的getter和setter(以类似于NSManagedObject在幕后的方式)。 根据我的理解,这可以使用resolveInstanceMethod:在特定的类上。 此时,您必须使用class_addMethod根据选择器动态添加方法。 我在理论层面上理解这一点,但我没有深入研究obj-c运行时,所以我很好奇是否有任何关于如何做到这一点的很好的例子。 我的大部分知识都来自这篇文章:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html
有什么想法/例子吗?
我所知道的唯一好讨论是Mike Ash的博客文章 。 实际上,这并不难。
我曾经需要将一个大的NSManagedObject
子类拆分为两个,但是决定保留一个实现细节,这样我就不必重写我的应用程序的其他部分了。 所以,我需要合成getter和setter,它会自动将[self foo]
发送到[self.data foo]
。
为此,我做了以下事情:
-
准备好已经在我class上的新方法。
- (id)_getter_ { return objc_msgSend(self.data, _cmd); } - (void)_setter_:(id)value { objc_msgSend(self.data, _cmd,value); }
请注意,
_cmd
包含选择器。 因此,通常,_cmd
在这些方法中是@selector(_getter_)
或@selector(_setter_)
,但我将插入_getter_
的实现作为foo
的实现。 然后,_cmd
包含_cmd
@selector(foo)
,因此调用self.data
的foo
。 -
写一个通用的合成方法:
+(void)synthesizeForwarder:(NSString*)getterName { NSString*setterName=[NSString stringWithFormat:@"set%@%@:", [[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]]; Method getter=class_getInstanceMethod(self, @selector(_getter_)); class_addMethod(self, NSSelectorFromString(getterName), method_getImplementation(getter), method_getTypeEncoding(getter)); Method setter=class_getInstanceMethod(self, @selector(_setter_:)); class_addMethod(self, NSSelectorFromString(setterName), method_getImplementation(setter), method_getTypeEncoding(setter)); }
请注意,这是一种类方法。 所以
self
代表了阶级。 另请注意,我没有硬编码类型编码(它告诉Objective-C运行时特定方法的参数是什么)。 记录类型编码的语法,但手工构建非常容易出错; 我浪费了几天,直到Mike Ash告诉我阻止它。 使用现有方法生成它。 -
尽早生成转发器:
+(void)load { for(NSString*selectorName in [NSArray arrayWithObjects:@"foo", @"bar", @"baz",nil]){ [self synthesizeForwarder:selectorName]; } }
这会生成
foo
,setFoo:
,bar
,setBar:
和baz
,setBaz:
.
希望这可以帮助!
另一个例子是我写的,名为DynamicStorage
,可在此处获得:
https://github.com/davedelong/Demos
它背后的主要推动力是这个问题 ,它询问如何使用NSMutableDictionary
作为任何对象ivar的后备存储。 我写了一个类,它将为任何@property
生成getter和setter,尊重自定义getter / setter名称,对象内存管理策略等等。关于它的imp_implementationWithBlock()
是它使用imp_implementationWithBlock()
以便它只有计算适当的属性名称一次(然后捕获并将其保存为块的一部分)。