12

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

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

我一直在阅读有关Cocoa的Archives and Serializations Programming Guide的文档。我知道您希望对对象的序列化进行一些控制,但这通常是一个对称的过程,必须反转为序列化编码的内容以反序列化它似乎很奇怪。我是 DRY 的信徒(不要重复自己)。

4

4 回答 4

12

这不仅是可能的,而且我有一个朋友正在努力做到这一点。(您可以在此处查看他的博客。)反射是使用Objective-C 2.0 运行时参考中记录的 Objective-C 运行时函数完成的。看一看。

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

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

于 2009-05-28T05:20:29.113 回答
6

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

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

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

尽管如此,我认为 Objective-C 甚至允许您首先做类似的事情,这真是太棒了。

于 2009-06-02T02:11:44.013 回答
0

查看 RMModelObject:

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

但是,请注意 ObjC 运行时的东西在 10.5 和模拟器中有效,但在实际的 iPhone 上无效。发现很难。也许 OS 3.0 最终支持它,但我还没有检查。

于 2009-07-02T23:18:36.603 回答
0

今晚写这篇文章就是为了这个目的;不要忘记#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);
}
于 2018-10-10T07:41:41.017 回答