如何在运行时向对象添加属性?

发布于 2024-12-10 11:23:03 字数 36 浏览 1 评论 0原文

是否可以在运行时向 Objective C 对象添加属性?

Is it possible to add properties to an Objective C object at runtime?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里兽 2024-12-17 11:23:03

可以通过 class_addProperty() 向类添加正式属性:

BOOL class_addProperty(Class cls,
    const char *name,
    const objc_property_attribute_t *attributes,
    unsigned int attributeCount)

前两个参数是不言自明的。第三个参数是属性属性数组,每个属性属性都是遵循 Objective-C 的名称-值对 类型编码 for 声明的属性。请注意,文档仍然提到了用于属性属性编码的逗号分隔字符串。以逗号分隔的字符串中的每个段都由一个 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方法应该更仔细地编写,但这应该足以作为如何在运行时动态添加形式属性的示例。

It’s possible to add formal properties to a class via 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.

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:

#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.

亚希 2024-12-17 11:23:03

如果您查看 NSKeyValueCoding 协议,记录了 这里,您可以看到有一条消息称为:

- (id)valueForUndefinedKey:(NSString *)key

您应该重写该方法以提供指定的未定义的自定义结果 财产。当然,这假设您的类使用相应的协议。

这种方法通常用于向类提供未知行为(例如,不存在的选择器)。

If you take a look at NSKeyValueCoding protocol, documented here, you can see that there is a message called:

- (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).

两相知 2024-12-17 11:23:03

@properties - 否(即使用点语法等)。但是您可以使用关联对象添加存储: 如何在对象内使用 objc_setAssociatedObject/objc_getAssociatedObject?

@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?.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文