【问题标题】:Dynamic Getters and Setters with Objective C使用 Objective C 的动态 Getter 和 Setter
【发布时间】:2011-07-17 00:30:24
【问题描述】:

我的情况是,我想在运行时为类动态生成 getter 和 setter(与 NSManagedObject 在幕后所做的类似)。据我了解,这可以在特定类上使用 resolveInstanceMethod: 。此时,您将不得不使用 class_addMethod 根据选择器动态添加方法。我在理论上理解这一点,但我没有深入研究 obj-c 运行时,所以我很好奇是否有任何很好的例子来说明如何做到这一点。我的大部分知识来自这篇文章:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html

有什么想法/例子吗?

【问题讨论】:

  • 从表面上看,这听起来很糟糕。你为什么要这样做?你遇到了什么问题?
  • 这实际上不是一个糟糕的主意:)。正如我所提到的,这里的目的是以类似于 NSManagedObject 的方式运行,其中子类对象可以按照其超类确定的方式对其属性进行操作。例如,对于 Core Data,超类定义了核心登录(包括动态 getter 和 setter)以确保数据存储在持久存储中。我的方法是完成类似的事情,但目的与 Core Data 完全不同(我无法在此处详细介绍,因为它与当前处于 NDA 下的功能相关联)。
  • @jtbandes 对 Apple 这么说 :)

标签: objective-c ios cocoa


【解决方案1】:

我知道的唯一一个很好的讨论是在 Mike Ash 的blog post。实际上,这并不难。

我曾经需要将一个大的 NSManagedObject 子类拆分为两个,但我决定将这一事实保留为实现细节,这样我就不必重写我的应用程序的其他部分。因此,我需要合成 getter 和 setter,它们会自动将 [self foo] 发送到 [self.data foo]

为此,我做了以下工作:

  1. 准备新方法,已经在我的课堂上。

    - (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 包含@selector(foo),因此调用self.datafoo

  2. 写一个通用的合成方法:

    +(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 运行时特定方法的参数是什么)。类型编码的语法已记录在案,但手工构建非常容易出错;我就这样浪费了几天,直到迈克·阿什告诉我停止它。使用现有方法生成它。

  3. 尽早生成转发器:

     +(void)load  
     {
         for(NSString*selectorName in [NSArray arrayWithObjects:@"foo", @"bar", @"baz",nil]){
            [self synthesizeForwarder:selectorName];
         }
     }
    

    这会生成 foosetFoo:barsetBar:bazsetBaz:

希望这会有所帮助!

【讨论】:

  • 太棒了。感谢您的快速响应!
【解决方案2】:

另一个例子是我写的,叫做DynamicStorage,可以在这里找到:

https://github.com/davedelong/Demos

它背后的主要推动力是this question,它询问如何使用NSMutableDictionary 作为任何对象ivar 的后备存储。我写了一个类,它将为任何@property 生成 getter 和 setter,尊重自定义 getter/setter 名称、对象内存管理策略等。关于它的巧妙之处在于它使用了imp_implementationWithBlock(),因此它只必须计算一次适当的属性名称(然后将其捕获并保存为块的一部分)。

【讨论】:

  • 很棒的东西戴夫。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 2023-03-12
  • 2014-05-29
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多