【问题标题】:Objective-C dynamic properties at runtime?运行时的Objective-C动态属性?
【发布时间】:2012-11-18 16:48:24
【问题描述】:

是否可以创建一个在运行时可以具有任意数量的动态属性的 Objective-C 类?

我希望能够调用mySpecialClass.anyProperty 并在我的类中拦截它,以便能够提供我自己的自定义实现,然后可以在运行时返回NSString(例如)并引发异常。显然这一切都必须编译。

如果我可以使用类似于新文字语法的东西来引用我的属性,例如mySpecialClass["anyProperty"].

我想在某种程度上我想创建一个没有 CFDictionary 后备存储的动态 NSDictionary,它分别在属性获取和设置上执行 2 个自定义方法,并将属性名称传递给这些访问器方法,以便他们可以决定什么去做。

【问题讨论】:

  • (有三种基本机制:1)实际向类添加方法,2)拦截“无法识别的消息”错误并提供实现,3)对于属性有一种特定的方法来拦截setter和吸气剂。但我手头没有任何细节。)

标签: objective-c


【解决方案1】:

你在问不同的事情。如果您希望能够在您的类的实例上使用括号语法mySpecialClass[@"anyProperty"],这非常容易。只需实现方法:

 - (id)objectForKeyedSubscript:(id)key
 {
      return ###something based on the key argument###
 }

 - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
 {
      ###set something with object based on key####
 }

每次在源代码中使用括号语法时都会调用它。

否则如果你想在运行时创建属性,有不同的方法可以继续,看看NSObjectforwardInvocation:方法,或者看看Objective-C Runtime Reference的函数来动态改变一个类.. .

【讨论】:

    【解决方案2】:

    纪尧姆是对的。 forwardInvocation: 是要走的路。这个答案提供了更多细节:method_missing-like functionality in objective-c (i.e. dynamic delegation at run time)

    这里有更多细节:Equivalent of Ruby method_missing in Objective C / iOS

    还有一些其他鲜为人知的 Obj-C 功能可能会对您有所帮助:Hidden features of Objective-C

    享受吧!

    【讨论】:

      【解决方案3】:

      至少有两种方法可以做到这一点。

      下标

      使用objectForKeyedSubscript:setObject:forKeyedSubscript:

       @property (nonatomic,strong) NSMutableDictionary *properties;
      
       - (id)objectForKeyedSubscript:(id)key {
            return [[self properties] valueForKey:[NSString stringWithFormat:@"%@",key]];
       }
      
       - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
            [[self properties] setValue:object forKey:[NSString stringWithFormat:@"%@",key]];
       }
      
       Person *p = [Person new];
       p[@"name"] = @"Jon";
       NSLog(@"%@",p[@"name"]);
      

      解决实例方法:

      这是运行时为所有方法执行的 objc_sendMsg:

      如果您查看底部,您有机会resolveInstanceMethod:,它可以让您将方法调用重定向到您选择的一个。要回答您的问题,您需要编写一个通用的 getter 和 setter 来查找字典 ivar 上的值:

      // generic getter
      static id propertyIMP(id self, SEL _cmd) {
          return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
      }
      
      
      // generic setter
      static void setPropertyIMP(id self, SEL _cmd, id aValue) {
      
          id value = [aValue copy];
          NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
      
          // delete "set" and ":" and lowercase first letter
          [key deleteCharactersInRange:NSMakeRange(0, 3)];
          [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
          NSString *firstChar = [key substringToIndex:1];
          [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
      
          [[self properties] setValue:value forKey:key];
      }
      

      然后实现resolveInstanceMethod:将请求的方法添加到类中。

      + (BOOL)resolveInstanceMethod:(SEL)aSEL {
          if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) {
              class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v@:@");
          } else {
              class_addMethod([self class], aSEL,(IMP)propertyIMP, "@@:");
          }
          return YES;
      }
      

      您也可以为该方法返回一个 NSMethodSignature,然后将其包装在一个 NSInvocation 中并传递给 forwardInvocation:,但添加该方法会更快。

      Here is a gist 在 CodeRunner 中运行。它不处理myClass["anyProperty"] 调用。

      【讨论】:

      • 代码来自 Rob Napier 和 Mugunth Kumar 的“iOS 5 Programming Pushing the Limits”。第 20 章:Deep Objective-C 解释了如何实现 @dynamic 属性。这本书有一些类似的先进技术。我使用 Omnigraffle 制作了图形,其中包含书中和几个地方的信息。
      • +1...as noted by Mike Ash,查找类型编码通常比硬编码更好。
      • 天哪,我一直在调整动态“属性桶”的实现,其中我的代码占其中的 90%,而你完成了它。 @Jano,一个大问题,如果属性不是对象,你会怎么做?换句话说,“id aValue”会给你一个 EXC_BAD_ACCESS?
      猜你喜欢
      • 2012-02-18
      • 1970-01-01
      • 2010-11-03
      • 2013-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多