If I want to pass an object that was created on the main thread onto an NSOperation object, what’s the standard way of doing to so that I’m not creating any memory management issues? Should I make my object’s properties not have the ‘nonatomic’ attribute?
Right now, I allocate the objects via [[[AClass alloc] init] autorelease], keep a copy of the instance on my main thread and then pass another copy into the NSOperation as part of an NSArray. When I try to iterate through the array list objects inside NSOperation class and access one of the AClass’s properties, the debugger reports that one of the member properties of AClass’s instance object is already zombied while others are not. The error I’m seeing is:
-[CFString retain]: message sent to deallocated instance 0x5a8c6b0
*** -[CFString _cfTypeID]: message sent to deallocated instance 0x5a8c6b0
*** -[CFString _cfTypeID]: message sent to deallocated instance 0x5a8c6b0
I can’t tell who is releasing my string properties too early but the entire object instance has not been released.
My class looks like:
@interface AClass
{
NSString *myTitle;
NSString *myDescription;
}
@property (nonatomic, retain, readonly) NSString *myTitle;
@property (nonatomic, retain, readonly) NSString *myDescription;
@end
@implementation AClass
@synthesize myTitle, myDescription;
- (void)dealloc
{
[myTitle release];
[myDescription release];
}
@end
Here’s an updated snippet for an efficient, ‘thread-safe’ version of AClass:
Beyond that, avoid overusing
autoreleasecalls so your memory issues are local to the callsite. This approach will solve many issues (although memory issues may still exist in your app).Update in response to questions:
actually, the
copycall to an immutable string could perform aretain.as an example:
AClasscould now implement@protocol NSCopyingby simplyretaining the 2 strings. also, if you knowAClassis never subclassed, you could just return[self retain]when the objects are allocated in the sameNSZone.if a mutable string is passed to the initializer of
AClass, then it will (of course) perform a concrete copy.if you want objects to share these strings, then this approach is preferred (in most cases) because you (and all clients using
AClass) now know the ivars will never change behind your back (what they point to, as well as the strings’ contents). of course, you still have the ability to make the mistake of changing whattitleanddescriptionpoint to in the implementation ofAClass– this would break the policy you’ve established. if you wanted to change what the members ofAClasspointed to, you’d have to use locks,@synchronizeddirectives (or something similar) – and then typically set up some observation callbacks so you could guarantee that your class works as expected… all that is unnecessary for most cases because the above immutable interface is perfectly simple for most cases.to answer your question: the call to
copyis not guaranteed to create a new allocation – it just allows several guarantees to propagate to clients, while avoiding all thread safety (and locking/synchronizing).now that i’ve detailed how copying immutable objects can be implemented. it should be obvious that properties of immutable objects (
NSString,NSNumber, etc.) should be declared copy in many cases (but many Cocoa programmers don’t declare them that way).if you want to share a
NSStringwhich you know is immutable, you should just copy it fromAClass.if you want to share an instance of
AClassyou have 2 choices:1) (best) implement
@protocol NSCopyingin AClass:- (id)copyWithZone:implementation added above.now the client is free to
copyandretainAClassas is most logical for their needs.2) (BAD) expect that all clients will keep their code up to date with changes to
AClass, and to usecopyorretainas required. this is not realistic. it is a good way to introduce bugs if your implementation ofAClassneeds to change because clients will not always update their programs accordingly. some people consider this acceptable when the object is private in a package (e.g., only one class uses and sees its interface).in short, it’s best to keep the
retainandcopysemantics predictable – and just hide all the implementation details in your class so your clients’ code never breaks (or is minimized).if your object is truly shared and its state is mutable, then use
retainand implement callbacks for state changes. otherwise, keep it simple and use immutable interfaces and concrete copying.if an object has an immutable state, then this example is always a lock free thread safe implementation with many guarantees.
for an implementation of an
NSOperationsubclass, i find it best (in most cases) to:– create an object which provides all the context it needs (e.g., an url to load)
– if the something needs to know about the operation’s result or to use the data, then create a
@protocolinterface for the callbacks and add a member to the operation subclass which is retained by the NSOperation subclass, and which you’ve prohibited from pointing to another object during the lifetime of the NSOperation instance: