【问题标题】:How do NSCoder and/or NSKeyedUnarchiver handle multiple decodings of the same object?NSCoder 和/或 NSKeyedUnarchiver 如何处理同一对象的多个解码?
【发布时间】:2011-09-26 23:46:09
【问题描述】:

我想知道NSCoder 在下次解码时如何处理由多个对象共享和编码的对象。它会复制对象的两个副本,还是将一个对象解码并在解码它的所有其他对象之间共享?

我在下面提供了一个类似这种情况的小例子。

例子:

  1. 应用程序启动
  2. 对象 A 和对象 B 将对象 C 设置为它们的委托
  3. 应用程序收到终止通知。
  4. 对象 A 和对象 B 编码它们自己和它们的所有对象(包括它们的委托)
  5. 应用程序关闭并重新启动
  6. 对象 A 和对象 B 解码它们自己和它们的所有对象(包括它们的委托)

在第 6 步之后,对象 A 和对象 B 是否会共享同一个解码对象,或者它们各自拥有自己的副本?

【问题讨论】:

    标签: objective-c macos cocoa-design-patterns nscoding nscoder


    【解决方案1】:

    它们将共享对同一对象的引用(除非您竭尽全力改变该行为)。

    即NSCoding 可以处理完全循环、全方位、复杂连接的对象图(只要所有图参与者都正确支持 NSCoding)。

    请注意,对委托进行编码是非常不典型的。委托通常连接到一个未归档的对象图anarchival 并且委托充当归档模型层(或归档视图层,在 IB 的情况下)之间的一种管道 - 故事更复杂,真的,对于 XIB 文件......但是......足够接近)和你的应用程序的其余部分。

    【讨论】:

    • 是的,我通常不会对委托进行编码。我只是想不出一个更好的例子。我想我本可以使用我的实际情况,但我打字比需要的多。无论如何,谢谢!
    • 不用担心——只是想确定一下。 :)
    【解决方案2】:

    这是一个很好的问题,我自己也一直想知道。所以,我写了一个小测试程序来尝试一下。四个类:ClassAClassBClassCMyBaseClassClassAClassBClassC 继承自 MyBaseClass,它符合 NSCoding 并提供两个属性:nameage。除了ClassAClassB 还包含对ClassC 实例的引用之外,A、B 和C 类是相同的。 ClassAClassB 还覆盖 initwithCoder:encodeWithCoder: 以解码和编码它们对 ClassC 实例的引用。顶级代码如下所示:

    ClassA *a = [[ClassA alloc] initWithName:@"Mr. A" age:11];
    ClassB *b = [[ClassB alloc] initWithName:@"Mrs. B" age:22];
    ClassC *c = [[ClassC alloc] initWithName:@"Ms. C" age:33];
    
    b.c = c;
    a.c = c;
    
    NSArray *rootObject = @[a, b, c];
    NSString *const kFilePath = @"/Users/myname/Documents/testarchive";
    BOOL result = [NSKeyedArchiver archiveRootObject:rootObject toFile:kFilePath];
    NSLog(@"result = %@", (result) ? @"YES" : @"NO");
    
    NSArray *newRootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:kFilePath];
    NSLog(@"new root object = %@", newRootObject);
    

    对象完美地序列化和反序列化。另外,反序列化后,a.cb.c 指向ClassC 的同一个实例——也就是说,它们的对象指针具有相同的地址。

    显然,在NSKeyedArchiverencodeObject:forKey: 中,进行了一项测试,以查看被编码的对象isEqualTo: 是否是先前编码的对象,如果是,则存储引用而不是完整的对象。相反的情况必须发生在NSKeyedUnarchiverdecodeObject:forKey: 中。很酷!

    【讨论】:

      【解决方案3】:

      我认为第一个答案并不完全正确。根据 Apple 的文档,“序列化只保留对象的值及其在层次结构中的位置。对同一个值对象的多次引用可能会在反序列化时产生多个对象”。

      因此,当从多个 NSCoders 反序列化时,不能保证单个对象序列化后会产生单个对象。

      如果您的实现与您的示例类似,那么您可能没有正确地考虑事情。如果您考虑应用程序的逻辑组织,那么多个对象可以共享同一个委托可能是有意义的。但通常我不希望有人使用 NSCoder 协议来编码/解码委托。通常我希望委托对它作为委托的对象进行编码/解码。

      例如,让我们看看 NSTableView。也许用户可以配置 NSTableView 的显示方式(也许用户可以调整列的大小或选择显示哪些列)。这是您可能希望使用 NSCoding 协议保存和恢复的有用信息。 NSTableView 也有代表。委托应该是一个控制器(来自 MVC 范例),并且永远不需要使用 NSCoding 进行编码/解码,因为它是不需要维护任何运行时状态的通用代码。

      因此,理想情况下,您使用 init 方法创建您的委托/控制器。它意识到它需要配置一个 NSTableView 以使其看起来与用户上次配置它时的方式相同,因此它使用 NSCoding 从磁盘中提取一个旧的表视图,然后将其显示给用户,就像他们最后一次看到它一样.

      我认为 MVC 范式中的模型层也是如此。同样,控制器层应该解码特定于用户通过使用应用程序所做的模型对象。

      这听起来更像是您试图从模型或视图层实例化控制器层。这真的没有意义。

      【讨论】:

      • 公平点,但没有回答关于归档程序行为的问题。可以是对问题的评论或简短的注释,例如bbum 的回答。
      • “对同一个值对象的多次引用可能会在反序列化时产生多个对象”。这些对象最好是 -isEqual: 和 -hash 相同。这应该在文档中说明。
      • 我的答案在第一段。
      • 您从 Apple 文档中引用的句子是指“序列化”,例如NSPropertyListSerialization,不要用NSCoder“归档”。这很清楚,例如通过这句话:'因为序列化不保留类信息或可变性,也不处理多个引用,编码(由 NSCoder 及其子类实现)是使对象图持久化的首选方式。此外,Eric 的问题中也没有提到 multiple NSCoders。
      猜你喜欢
      • 1970-01-01
      • 2016-02-15
      • 2020-03-20
      • 1970-01-01
      • 2020-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-26
      相关资源
      最近更新 更多