是否可以创建一个在运行时可以拥有任意数量的动态属性的Objective-C类?
我希望能够调用mySpecialClass.anyProperty
并在我的类中拦截它,以便能够提供我自己的自定义实现,然后可以在运行时返回NSString
(例如),引发异常。显然这一切都要编译。
理想的情况是,如果我可以使用类似于新文字语法的内容来引用我的属性,例如mySpecialClass["anyProperty"]
。
我想在某种程度上我想创建类似动态NSDictionary的东西,没有CFDictionary后备存储,它分别在属性获取和设置上执行2个自定义方法,属性名称传入这些访问器方法,以便他们可以决定什么要做。
答案 0 :(得分: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"]);
这是运行时为所有方法执行的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。它不处理myClass["anyProperty"]
次呼叫。
答案 1 :(得分: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
的{{1}}方法,或查看Objective-C Runtime Reference的函数动态改变一个班级......
答案 2 :(得分:1)
Guillaume是对的。 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
享受!