【发布时间】:2011-12-10 18:29:37
【问题描述】:
是否可以在运行时向 Objective C 对象添加属性?
【问题讨论】:
-
如果它符合KVC协议-有可能
-
属性到底是什么意思? Objective-C 声明的属性?
标签: objective-c cocoa properties objective-c-runtime
是否可以在运行时向 Objective C 对象添加属性?
【问题讨论】:
标签: objective-c cocoa properties objective-c-runtime
@properties - 否(即使用点语法等)。但是您可以使用关联对象添加存储:How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?。
【讨论】:
如果你看一下NSKeyValueCoding协议,记录在here,你可以看到有一条消息叫做:
- (id)valueForUndefinedKey:(NSString *)key
您应该重写该方法,以便为指定的未定义属性提供自定义结果。当然这假设你的类使用了相应的协议。
这种方法通常用于向类提供未知行为(例如,不存在的选择器)。
【讨论】:
可以通过class_addProperty()向类添加形式属性:
BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
前两个参数是不言自明的。第三个参数是一个属性属性数组,每个属性属性是一个名称-值对,遵循 Objective-C type encodings 为 declared 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 方法应该更仔细地编写,但这应该足以作为如何在运行时动态添加正式属性的示例。
【讨论】:
-name 选择器。
[self performSelector:NSSelectorFromString(@"retain")] 代替[self retain]。 ARC 是一项安全功能,而不是一项安全功能!而且 -performSelector 并不比仅仅声明选择器恕我直言更优雅。
[o name] 给出编译器错误'No known instance method for selector 'name'' => 这是一个 ARC 问题。没有 ARC,调用[o name] 是可能的,这就是我所说的。使用 ARC,您必须做一些不同的事情,例如-performSelector:,或声明-name 选择器。明白吗?