12

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

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

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

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

4

3 回答 3

34

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

下标

使用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:

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:,但添加该方法更快。

这是在 CodeRunner 中运行的要点。它不处理myClass["anyProperty"]呼叫。

于 2012-11-30T13:56:21.543 回答
3

你问的是不同的事情。如果您希望能够在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####
 }

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

否则,如果您想在运行时创建属性,有不同的方法可以继续,查看NSObject'sforwardInvocation:方法,或查看Objective-C 运行时参考以获取动态更改类的函数...

于 2012-11-30T13:09:04.407 回答
1

纪尧姆是对的。forwardInvocation:是要走的路。这个答案提供了更多细节:objective-c中的method_missing-like功能(即运行时的动态委托)

这有更多细节:Objective C / iOS 中 Ruby method_missing 的等价物

这些是其他一些鲜为人知的 Obj-C 特性,它们可能会对您有所帮助:Objective-C 的隐藏特性

享受!

于 2012-11-30T13:14:59.943 回答