【问题标题】:How can I add properties to an object at runtime?如何在运行时向对象添加属性?
【发布时间】:2011-12-10 18:29:37
【问题描述】:

是否可以在运行时向 Objective C 对象添加属性?

【问题讨论】:

  • 如果它符合KVC协议-有可能
  • 属性到底是什么意思? Objective-C 声明的属性?

标签: objective-c cocoa properties objective-c-runtime


【解决方案1】:

@properties - 否(即使用点语法等)。但是您可以使用关联对象添加存储:How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?

【讨论】:

    【解决方案2】:

    如果你看一下NSKeyValueCoding协议,记录在here,你可以看到有一条消息叫做:

    - (id)valueForUndefinedKey:(NSString *)key
    

    您应该重写该方法,以便为指定的未定义属性提供自定义结果。当然这假设你的类使用了相应的协议。

    这种方法通常用于向类提供未知行为(例如,不存在的选择器)。

    【讨论】:

      【解决方案3】:

      可以通过class_addProperty()向类添加形式属性:

      BOOL class_addProperty(Class cls,
          const char *name,
          const objc_property_attribute_t *attributes,
          unsigned int attributeCount)
      

      前两个参数是不言自明的。第三个参数是一个属性属性数组,每个属性属性是一个名称-值对,遵循 Objective-C type encodingsdeclared properties。请注意,文档仍然提到用于属性属性编码的逗号分隔字符串。逗号分隔字符串中的每个段都由一个 objc_property_attribute_t 实例表示。此外,除了id 的通用@ 类型编码之外,objc_property_attribute_t 还接受类名。

      这是一个程序的初稿,该程序将一个名为 name 的属性动态添加到一个已经有一个名为 _privateName 的实例变量的类中:

      #include <objc/runtime.h>
      #import <Foundation/Foundation.h>
      
      @interface SomeClass : NSObject {
          NSString *_privateName;
      }
      @end
      
      @implementation SomeClass
      - (id)init {
          self = [super init];
          if (self) _privateName = @"Steve";
          return self;
      }
      @end
      
      NSString *nameGetter(id self, SEL _cmd) {
          Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
          return object_getIvar(self, ivar);
      }
      
      void nameSetter(id self, SEL _cmd, NSString *newName) {
          Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
          id oldName = object_getIvar(self, ivar);
          if (oldName != newName) object_setIvar(self, ivar, [newName copy]);
      }
      
      int main(void) {
          @autoreleasepool {
              objc_property_attribute_t type = { "T", "@\"NSString\"" };
              objc_property_attribute_t ownership = { "C", "" }; // C = copy
              objc_property_attribute_t backingivar  = { "V", "_privateName" };
              objc_property_attribute_t attrs[] = { type, ownership, backingivar };
              class_addProperty([SomeClass class], "name", attrs, 3);
              class_addMethod([SomeClass class], @selector(name), (IMP)nameGetter, "@@:");
              class_addMethod([SomeClass class], @selector(setName:), (IMP)nameSetter, "v@:@");
      
              id o = [SomeClass new];
              NSLog(@"%@", [o name]);
              [o setName:@"Jobs"];
              NSLog(@"%@", [o name]);
          }
      }
      

      它的(修剪后的)输出:

      Steve
      Jobs
      

      getter 和 setter 方法应该更仔细地编写,但这应该足以作为如何在运行时动态添加正式属性的示例。

      【讨论】:

      • class_addProperty 返回 true,但 class_getInstanceVariable 始终返回 nil。我尝试使用属性名称而不是 ivar 名称,但仍然没有运气。知道可能是什么问题吗?
      • @Bavarious,你是如何愚弄编译器的?我的意思是 [o name] 导致编译错误 'No known instance method for selector 'name''。
      • @HiteshSavaliya 很久以前(在 ARC 之前)这是可能的。现在你至少必须声明 -name 选择器。
      • @PercevalFARAMAZ:是的,因为 ARC 主要是一个编译时特性......并且 -performSelector: 已被声明并且对编译器可见。在 ARC 之前,您可以在没有 -performSelector: 的情况下执行此操作,而使用 ARC 则不能。这就是我所说的,它仍然是真的。有 100 种方法可以规避每个 ARC 限制,例如调用[self performSelector:NSSelectorFromString(@"retain")] 代替[self retain]。 ARC 是一项安全功能,而不是一项安全功能!而且 -performSelector 并不比仅仅声明选择器恕我直言更优雅。
      • @PercevalFARAMAZ:如果您在编译时不知道选择器,则必须使用 -performSelector: 之类的东西,不管 ARC 是什么。这里最初的讨论是关于[o name] 给出编译器错误'No known instance method for selector 'name'' => 这是一个 ARC 问题。没有 ARC,调用[o name] 是可能的,这就是我所说的。使用 ARC,您必须做一些不同的事情,例如-performSelector:,或声明-name 选择器。明白吗?
      猜你喜欢
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-25
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      相关资源
      最近更新 更多