运行时的Objective-C动态属性?

lms*_*lms 11 objective-c

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

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

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

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

Jan*_*ano 32

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

下标

使用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"]);
Run Code Online (Sandbox Code Playgroud)

resolveInstanceMethod:

这是运行时为所有方法执行的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];
}
Run Code Online (Sandbox Code Playgroud)

然后实现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;
}
Run Code Online (Sandbox Code Playgroud)

您还可以为该方法返回NSMethodSignature,然后将其包装在NSInvocation中并传递给forwardInvocation:该方法,但添加方法的速度更快.

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

  • 我想指出这个答案的精彩之处. (5认同)
  • + 1 ... [由Mike Ash指出](http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html),查找类型编码通常更好,而不是硬编码. (2认同)