Memory management related to void* context in Cocoa
There are a number of Foundation classes that allow you to provide a pointer to void that is passed as an argument to a callback function at a later time. For in开发者_如何学Gostance, addObserver:forKeyPath:options:context: of NSKeyValueObserving.
Since a pointer to void may not extend NSObject, functions which accept such an argument cannot be expected to retain it. Therefore, your code must look similar to the following:
- (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
}
This makes sense, although it seems to violate every principle of Cocoa/Objective-C object ownership. In addition, if you forget to manually manage memory in such a case it often (although not always) results in a EXC_BAD_ACCESS crash. Furthermore, the Xcode analyzer complains.
The reason I am asking this question is because I am writing a network connection library that makes use of the same sort of context pointers in its public API. However, after having had to track down a number of memory management bugs in my own code related to the resulting need for manual memory management, I believe there must be a better way. One solution is to change the types of the context arguments of my API to (id<NSObject>) rather than (void*).
Why do Foundation classes often use (void*) rather than (id<NSObject>)?
MHC is correct about the reasoning. There is no requirement that the object be an NSObject. But you are doing your memory management incorrectly here in any case, as you note.
Rather than leaking an object, and then trying to clean it up later, the common pattern for managing an object in this case is to store it as an ivar of the registering class. Another common pattern is to pass self
as the context, making sure to unregister for callbacks during -dealloc
.
Because it is not always id. Data could be a C structure, or a Core Foundation object, or even a scalar.
精彩评论