在 encodeWithCoder: 和 initWithCoder: 之后指向现有对象的指针:

Pointers to existing objects after encodeWithCoder: and initWithCoder:

NSCoding 如何处理指向其他对象而不是值的指针的编码和解码?我有一系列模型 类 需要编码,它们需要使用弱属性相互引用。我惊讶地发现,在编码、保存到文件、随后从文件中读取和解码之后,引用似乎得到了维护。即使相互引用的对象的内存地址发生变化,这仍然有效。我很想知道 NSCoding 如何实现这一点,也想确保它能够始终如一地工作。

简而言之:如果我对包含指向其他对象的指针的属性进行编码,我能否 100% 依赖解码后维护的引用?

tl;dr: 是的,NSKeyedArchiverNSKeyedUnarchiver 维护对象图中对象的引用关系;如果你对一个对象图进行编码,其中对象引用其他对象,你可以期望以维护这些关系的方式对图进行解码。你不一定会在编码和解码后得到相同的指针,但对象的身份将保持不变。


当您通过 -encodeObject:forKey: 对对象进行编码时,NSKeyedArchiver 永远不会写出对象的指针地址。相反,它维护一个唯一的 table 对象,它以前见过:

  1. 如果您正在编码的对象以前从未见过,它会被添加到 table 并分配一个 UID。然后它有 -encodeWithCoder: 调用它所以它可以编码它的所有属性
  2. 如果它之前被看到过(并且已经分配了一个 UID),则什么也不会发生(但是 UID 被记录在它的位置)

归档完成后,归档包含唯一的 table 对象,以及表示对象图结构的另一个字典。代替对象的是 UID。

在解码时,当你调用 -decodeObject:forKey: 时,NSKeyedUnarchiver 执行相反的映射:

  1. 它查看存档中密钥的 UID,并在唯一 table 中搜索 UID。如果该对象之前已被解码,则它 returns 是对已解码对象的引用。这是关键部分——您不一定会在解码时获得与在编码时编码相同的 pointer,但您会获得对相同 object 而不是副本
  2. 如果该UID之前从未被解码过,则在该UID对应的唯一table中查找编码对象,查找class并调用+alloc-initWithCoder:,以及对象上的 -awakeAfterUsingCoder。然后将对象记录在 UID 到对象映射中,以便在必要时重复使用

这是键控归档系统的主要功能之一,您可以放心地维护此行为。