通用 NSCoding 实现的 Objective-C Reflection

发布于 2024-07-23 04:49:14 字数 573 浏览 6 评论 0原文

Objective-C 中是否有任何反射方法允许您通过检查对象的公共属性并生成 encodeWithCoder: 和 initWithCoder: 的通用实现来编写通用 NSCoding 实现。

我正在考虑类似 XStream for Java 的东西,它允许使用通用方法来序列化和反序列化 Java使用反射的对象。 更好的方法可能是将属性标记为您想要序列化的东西或瞬态的东西(如 Java 中的瞬态关键字)。

我一直在阅读 档案 和Cocoa 序列化编程指南。 我知道您希望对对象的序列化进行一些控制,但这通常是一个对称的过程,并且必须反转序列化编码以反序列化它似乎很奇怪。 我是 DRY 的信徒(不要重复自己)。

Is there any means of reflection in Objective-C that would allow you to write generic NSCoding implementations by inspecting the public properties of an object and generating generic implementations of encodeWithCoder: and initWithCoder: .

I'm thinking of something like XStream for Java that allows a generic way to serialize and deserialize Java objects using reflection. Even better would probably be some means of marking properties as things you'd want to serialize or that are transient (like the transient keyword in Java).

I've been reading the documentation on Archives and Serializations Programming Guide for Cocoa. I understand that you want some control over the serialization of your objects, but it is generally a symmetrical process and it seems odd to have to reverse what is coded for serialization to deserialize it. I'm a believer of DRY (don't repeat yourself).

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

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

发布评论

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

评论(4

谈下烟灰 2024-07-30 04:49:14

这不仅是可能的,而且我有一个朋友正在尝试这样做。 (您可以在此处查看他的有关此内容的博客.) 反射是使用 Objective-C 2.0 运行时参考。 看一看。

但请注意,只有当您想要保存所有实例变量的通用行为时,这才有效。 不过,您可能不希望 NSView 保存其超级视图; 在这种情况下,通用情况将不起作用。

您可以通过为要保存的任何实例变量声明属性并保留任何其他变量“隐藏”来区分要序列化的事物和不可序列化的事物,但这将属性的整个目的扭曲到一个小小的好处。 我不会推荐它。

Not only is it possible, but I have a friend who's taken a stab at doing precisely that. (You can see his blog about it here.) The reflection is done using the Objective-C runtime functions documented in the Objective-C 2.0 Runtime Reference. Take a look.

Note, however, that this will only work if you want the generic behavior of saving all the instance variables. You might not want an NSView to save its superview, though; in such cases, the generic case wouldn't work.

You could conceivably distinguish between things-to-serialize and things-not-to-serialize by declaring properties for any instance variables you want to save and leaving any other variables "hidden", but that's twisting the whole purpose of properties to a small benefit. I wouldn't recommend it.

聆听风音 2024-07-30 04:49:14

(我是 BJ Homer 评论中链接的博客文章的作者)。 使用代码有几个注意事项:

  1. 目标类必须符合 KVC。 如果您为 ivars 使用 Objective-C 2.0 属性,那么您就已经准备好了。 否则,您需要确保您的类设置为正确响应 valueForKey: 和 setValue:forKey: 方法。
  2. 该代码使用运行时的未记录的“功能”来递归地使 ivar 类符合 NSCoding。 编译时,几乎所有类型信息 (AFAIK) 都会从代码中删除,但为了保存 ivars,它们也必须符合 NSCoding。 我发现,如果 ivar 是一个对象,并且我对其调用 ivar_getTypeEncoding(),我将得到类似以下内容的 ac 字符串:{#IVAR_CLASS}(示例:{#NSString})。 我所做的就是获取 # 和 } 之间的字符串,使用 NSClassFromString 创建一个 Class 对象,然后对其进行递归。
  3. 此代码将保存所有 ivars。
  4. 使用风险自负(当然)

我写这篇文章主要是为了概念证明,在很多情况下,这段代码会严重失败。 例如,在对象 A 中,B 有一个 ivar,而 B 有 A 的 ivar,然后使用代码序列化其中一个或另一个(我相信)会导致无限循环。

尽管如此,我认为 Objective-C 甚至允许你做像一开始那样的事情,这真是太棒了。

(I am the author of the blog post linked to in BJ Homer's comment). There are a couple caveats to using the code:

  1. The target class has to be KVC-compliant. If you're using Objective-C 2.0 properties for your ivars, then you're all set. Otherwise, you need to make sure that your class is set up to properly respond to valueForKey: and setValue:forKey: methods.
  2. The code uses an undocumented "feature" of the runtime to recursively conform ivar classes to NSCoding. When compiled, almost all typing information (AFAIK) is stripped out of the code, yet in order to save the ivars, they have to conform to NSCoding as well. I discovered that if the ivar is an object and I call ivar_getTypeEncoding() on it, I'll get a c string back that looks something like: {#IVAR_CLASS} (Example: {#NSString}). What I do is get the string between the # and }, create a Class object using NSClassFromString, and recurse on that.
  3. This code will save ALL the ivars.
  4. Use at your own risk (of course)

I wrote this mainly as a proof of concept, and there are many instances when this code would fail spectacularly. For example, in object A has an ivar for B, and B has an ivar for A, then using the code to serialize one or the other would (I believe) cause an infinite loop.

Nevertheless, I think it's pretty freaking awesome that Objective-C even allows you to do stuff like in the first place.

表情可笑 2024-07-30 04:49:14

查看 RMModelObject:

http://www.realmacforge.com/svn/trunk/RMModelObject/< /a>

但是,请注意 ObjC 运行时功能可以在 10.5 和模拟器中运行,但不能在实际的 iPhone 上运行。 艰难地发现了这一点..也许OS 3.0终于支持它了,但我还没有检查过。

Check out RMModelObject:

http://www.realmacforge.com/svn/trunk/RMModelObject/

However, note that ObjC Runtime stuff works in 10.5, and in the Simulator, but not on an actual iPhone. Found that out the hard way.. Maybe OS 3.0 finally supports it, but I haven't checked.

臻嫒无言 2024-07-30 04:49:14

今晚就是为了这个目的写这篇文章的; 不要忘记#import! 这假设类中的所有 @properties 都是 NSObject 形式,因此 NSNumber 而不是 float 等。

#import <objc/runtime.h>

-(id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        uint count;
        objc_property_t *properties = class_copyPropertyList(self.class, &count);
        for (int i = 0; i < count ; i++) {
            const char* propertyName = property_getName(properties[i]);
            NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
            NSValue *value = [decoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(properties);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    uint count;
    objc_property_t *properties = class_copyPropertyList(self.class, &count);
    for (int i = 0; i < count ; i++) {
        const char* propertyName = property_getName(properties[i]);
        NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        NSValue *value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(properties);
}

Wrote this tonight for this very purpose; don't forget the #import! This assumes all @properties in your class are NSObject-form, so NSNumber instead of float, etc.

#import <objc/runtime.h>

-(id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        uint count;
        objc_property_t *properties = class_copyPropertyList(self.class, &count);
        for (int i = 0; i < count ; i++) {
            const char* propertyName = property_getName(properties[i]);
            NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
            NSValue *value = [decoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(properties);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    uint count;
    objc_property_t *properties = class_copyPropertyList(self.class, &count);
    for (int i = 0; i < count ; i++) {
        const char* propertyName = property_getName(properties[i]);
        NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        NSValue *value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(properties);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文