在运行时给Objective-C对象添加属性是否可行?
在运行时给Objective-C对象添加属性是否可行?
class_addProperty()
,可以为类添加正式属性:BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
objc_property_attribute_t
实例表示。此外,objc_property_attribute_t
还接受类名,而不仅仅是通用的@
类型编码的id
。_privateName
的实例变量的类添加一个名为name
的属性的程序的初稿:#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
获取器和设置器方法应该更加小心地编写,但这已经足够作为在运行时动态添加正式属性的示例。
-name
选择器。 - Michael[self performSelector:NSSelectorFromString(@"retain")]
代替[self retain]
。ARC是一个安全特性,而不是安全特性!在我看来,-performSelector并不比声明选择器更优雅。 - Michael[o name]
导致编译器错误“没有已知的实例方法选择器'name'”,这是一个ARC问题。没有ARC,调用[o name]
是可行的,这就是我说的。使用ARC,你必须做一些不同的事情,比如-performSelector:,或者声明-name选择器。明白了吗? - Michael如果您查看文档这里所记录的NSKeyValueCoding
协议,您会发现有一个称为:
- (id)valueForUndefinedKey:(NSString *)key
@properties - 不使用(即使用点语法等)。但是,您可以使用关联对象来添加存储:如何在对象内部使用objc_setAssociatedObject / objc_getAssociatedObject?。