内存pipe理与cocoa中的void *上下文有关

有许多基础类允许你提供一个指向void的指针,该指针稍后作为parameter passing给一个callback函数。 例如,对于NSKeyValueObserving的addObserver:forKeyPath:options:context:。

由于指向void的指针可能无法扩展NSObject,因此接受这种参数的函数不能期望保留它。 因此,您的代码必须看起来类似于以下内容:

- (void)sharedInit { MyObject *myObject = [[MyObject alloc] init]; [x addObserver:y forKeyPath:@"z" options:0 context:myObject]; // cannot (auto)release myObject here } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { MyObject *myObject = (MyObject *)context; [myObject release]; // myObject is released here } 

这是有道理的,尽pipe它似乎违反了Cocoa / Objective-C对象所有权的每一个原则。 另外,如果您忘记在这种情况下手动pipe理内存,则经常(虽然不总是)导致EXC_BAD_ACCESS崩溃。 此外,Xcode分析仪抱怨。

我问这个问题的原因是因为我正在编写一个networking连接库,在其公共API中使用相同types的上下文指针。 然而,在我自己的代码中find了一些与手动内存pipe理相关的内存pipe理错误之后,我相信必须有更好的方法。 一个解决scheme是将我的API的上下文参数的types改为(id <NSObject>)而不是(void *)。

为什么基础类经常使用(void *)而不是(id <NSObject>)?

MHC对于推理是正确的。 没有要求该对象是一个NSObject。 但是正如你注意到的那样,你在这里无论如何都在错误地进行内存pipe理。

而不是泄漏一个对象,然后尝试清理它,在这种情况下pipe理对象的通用模式是将其存储为注册类的ivar。 另一个常见的模式是将self作为上下文,确保在-dealloc期间取消注册callback。

因为它并不总是id。 数据可以是C结构,也可以是Core Foundation对象,甚至是标量。