实例化对象时,向已释放对象发送 NSKeyValueNotifyObserver 消息时如何发生 BAD ACCESS

When an object is being instantiated, how can a BAD ACCESS occur when sending a NSKeyValueNotifyObserver message to a deallocated object

我们遇到了这个奇怪的问题。 当我们实例化一个对象时,我们也实例化了一个属于那个对象的属性:

-(instancetype)init
{
   self = [super init];
   if (self) {
     [self setDocument];
   }
   return self;
}

-(void)setDocument:
{
   _flatGraphicsArrayController = [[NSArrayController alloc] initWithContent:doc.flattenedObjects];
}

...偶尔 EXC_BAD_ACCESS 发生在 _flatGraphicsArrayController

的设置

调用堆栈:

已确定此崩溃是由将 NSKeyValueNotifyObserver 消息发送到已释放对象引起的,该对象似乎正在观察 flatGraphicsArrayController

的变化

对我来说,这非常令人困惑,因为拥有这个 属性 的对象刚刚被实例化,所以怎么可能观察到 属性 的变化?

是否有人注册观察特定的内存地址(如果它是这样工作的),然后 flatGraphicsArrayController 以某种方式在内存中获取了那个 space,而观察者被释放了?

某个对象 (Object1) 作为观察者添加到另一个对象 (Object2)。在那之后的某个时间,Object1 和 Object2 都被释放,但没有任何东西将 Object1 作为 Object2 的观察者移除。键值观察者的关系保持在任一对象之外(因为当添加 KVO 时,出于二进制兼容性原因,不能向 NSObject 添加新的实例变量,因此它必须将其状态存储在一侧 table).

KVO 应该在释放 Object1 时抱怨这个。检查控制台日志。

无论如何,稍后您将创建 NSArrayController 的实例。它恰好与 Object2 占用相同的地址。这意味着它匹配 KVO 的关于 Object1 和 Object2 之间的观察关系的内部信息。因此,实际上,已失效的 Object1 现在正在观察您的阵列控制器。当它的属性改变时,它向 Object1 发送 KVO 改变通知。当然,Object1 已经不存在了。根据其地址是否已被重用以及该地址是新对象的基地址还是指向其中的某处,结果可能是崩溃或无声。

要解决此问题,您需要始终在释放观察对象或观察对象之前删除 KVO 观察。