Is it possible to add properties to an Objective C object at runtime?
是否可以在运行时向目标C对象添加属性?
3 个解决方案
#1
60
It’s possible to add formal properties to a class via class_addProperty()
:
可以通过class_addProperty()向类添加形式属性:
BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
The first two parameters are self-explanatory. The third parameter is an array of property attributes, and each property attribute is a name-value pair which follow Objective-C type encodings for declared properties. Note that the documentation still mentions the comma-separated string for the encoding of property attributes. Each segment in the comma-separated string is represented by one objc_property_attribute_t
instance. Furthermore, objc_property_attribute_t
accepts class names besides the generic @
type encoding of id
.
前两个参数是不言自明的。第三个参数是属性属性的数组,每个属性属性都是一个名称-值对,它遵循被声明属性的Objective-C类型编码。注意,文档仍然提到了属性属性编码的逗号分隔字符串。逗号分隔字符串中的每个段由一个objc_property_attribute_t实例表示。此外,objc_property_attribute_t除了接受id的通用@类型编码外,还接受类名。
Here’s a first draft of a program that dynamically adds a property called name
to a class that already has an instance variable called _privateName
:
下面是一个程序的初稿,它动态地向已经有一个名为_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]);
}
}
Its (trimmed) output:
(修剪)输出:
Steve
Jobs
The getter and setter methods should be written more carefully but this should be enough as an example of how to dynamically add a formal property at runtime.
应该更仔细地编写getter和setter方法,但这应该足以作为在运行时动态添加正式属性的示例。
#2
8
If you take a look at NSKeyValueCoding
protocol, documented here, you can see that there is a message called:
如果你看一下NSKeyValueCoding协议,这里有文档,你可以看到有一条消息叫做:
- (id)valueForUndefinedKey:(NSString *)key
You should override that method to provide your custom result for the specified undefined property. Of course this assumes that your class uses the corresponding protocol.
您应该重写该方法,为指定的未定义属性提供自定义结果。当然,这假定您的类使用了相应的协议。
This kind of approach is commonly uses to provide unknown behavior to classes (eg. a selector that doesn't exist).
这种方法通常用于向类提供未知的行为(例如。一个不存在的选择器)。
#3
4
@properties - no (i.e. using dot syntax etc). But you can add storage using using associated objects: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?.
@properties - no(例如使用点语法等)。但是您可以使用关联对象来添加存储:我如何在对象中使用objc_setAssociatedObject/objc_getAssociatedObject ?
#1
60
It’s possible to add formal properties to a class via class_addProperty()
:
可以通过class_addProperty()向类添加形式属性:
BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
The first two parameters are self-explanatory. The third parameter is an array of property attributes, and each property attribute is a name-value pair which follow Objective-C type encodings for declared properties. Note that the documentation still mentions the comma-separated string for the encoding of property attributes. Each segment in the comma-separated string is represented by one objc_property_attribute_t
instance. Furthermore, objc_property_attribute_t
accepts class names besides the generic @
type encoding of id
.
前两个参数是不言自明的。第三个参数是属性属性的数组,每个属性属性都是一个名称-值对,它遵循被声明属性的Objective-C类型编码。注意,文档仍然提到了属性属性编码的逗号分隔字符串。逗号分隔字符串中的每个段由一个objc_property_attribute_t实例表示。此外,objc_property_attribute_t除了接受id的通用@类型编码外,还接受类名。
Here’s a first draft of a program that dynamically adds a property called name
to a class that already has an instance variable called _privateName
:
下面是一个程序的初稿,它动态地向已经有一个名为_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]);
}
}
Its (trimmed) output:
(修剪)输出:
Steve
Jobs
The getter and setter methods should be written more carefully but this should be enough as an example of how to dynamically add a formal property at runtime.
应该更仔细地编写getter和setter方法,但这应该足以作为在运行时动态添加正式属性的示例。
#2
8
If you take a look at NSKeyValueCoding
protocol, documented here, you can see that there is a message called:
如果你看一下NSKeyValueCoding协议,这里有文档,你可以看到有一条消息叫做:
- (id)valueForUndefinedKey:(NSString *)key
You should override that method to provide your custom result for the specified undefined property. Of course this assumes that your class uses the corresponding protocol.
您应该重写该方法,为指定的未定义属性提供自定义结果。当然,这假定您的类使用了相应的协议。
This kind of approach is commonly uses to provide unknown behavior to classes (eg. a selector that doesn't exist).
这种方法通常用于向类提供未知的行为(例如。一个不存在的选择器)。
#3
4
@properties - no (i.e. using dot syntax etc). But you can add storage using using associated objects: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?.
@properties - no(例如使用点语法等)。但是您可以使用关联对象来添加存储:我如何在对象中使用objc_setAssociatedObject/objc_getAssociatedObject ?