Hello
I have a problem with the following code in a GTMTestCase:
- (void)testSomething {
myType *year = [myType valueFromString:#"1978"];
STAssertTrue([year isKindOfClass:[XBNumberAttribute class]], #"Must be subtype.");
}
If I build this (=execute the tests), I get a "segmentation fault "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" -RegisterForSystemEvents, Command /bin/sh failed with exit code 139 " error. This however goes away as soon as I retain the year object (which is actually autoreleased in the valueFromString method, see below):
- (void)testSomething {
myType *year = [[myType valueFromString:#"1978"] retain];
//STAssertTrue(([year retainCount] == 2), #"Retain count wrong");
STAssertTrue([year isKindOfClass:[XBNumberAttribute class]], #"Must be subtype.");
}
Uncommenting the retainCount assertion indeed shows that the retain count is 2 at this point. However, if I put [year release] at the end of the method, the build fails again with the same error as explained above.
What is the matter here?
For the sake of completeness I include the code of valueFromString:
+ (id)valueFromString:(NSString *)pString {
return [[[myType alloc] initWithString:pString] autorelease];
}
And here the initWithString method:
- (id)initWithString:(NSString *)pString {
if (self = [super initWithString:pString]){
}
return self;
}
pointing to the following super type method:
- (id)initWithString:(NSString *)pString
{
if (self = [super init]) {
theNumber = [NSNumber numberWithInt:[pString intValue]];
}
return self;
}
After switching on all the autorelease debug options (see http://www.cocoabuilder.com/archive/cocoa/87251-how-do-debug-an-autorelease-crash.html), I found the solution:
The variable theNumber is not accessed via the property accessor, so it is not actually retained (in the initWithString of the super type method, last code snippet). As it is however receiving a release in this type's dealloc method because it is marked as a #property (retain) (information not included here, but important!), I got an error when the autorelease of the child object was called (first code snippet): it released the supertype and with it the attribute which was not there anymore.
Related
- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
if (someInstance == aSomeInstanceValue)
{
return;
}
SomeClass *oldValue = someInstance;
someInstance = [aSomeInstanceValue retain];
[oldValue release];
}
ok, so setter should look like. I understand first 3 lines - prevent before situation when new object is the same as the old one.
But what about this line:
SomeClass *oldValue = someInstance;
Why system have to keep address of old object. Why can't it be simply
[someinstance release];
someinstance = [aSomeInstanceValue retain];
Actually - no reason.
It's usually just a choice.
There are three idioms for writing accessors.
Autorelease:
- (void)setFoo:(id)newFoo {
[foo autorelease];
foo = [newFoo retain];
}
Less code to write, but I think autorelease in this case is being lazy.
Retain then release
- (void)setFoo:(id)newFoo {
[newFoo retain];
[foo release];
foo = newFoo;
}
Check first
- (void)setFoo:(id)newFoo {
if ([foo isEqual:newFoo]) {
return;
}
[foo release];
foo = [newFoo retain];
}
The only difference between the last two is that the second checks to see if the new value is different to the current value before trying to set the property. At the cost of an extra if statement. So - if the new value is likely to be the same as the old value, using this construction gives better performance.
Generally, and if you're not using properties for some strange reason, use retain then release, and then if profiling shows that there's a bottleneck - use the check first method.
I would suggest the default retain setter works something like this:
- (void) setFoo:(id) foo {
if ( foo == _foo) return;
[_foo release];
_foo = [foo retain];
}
if you don't check if the old and the new foo are the same, you might end up with a reference to a deallocated object if you for some reason write something like this:
myObject.foo = myObject.foo;
Because the same object would first be released, and then retained. If the myObject is the sole owner, the object would be deallocated after the first release, leaving you with a dangling pointer.
The default retain setter works like that :
- (void)setFoo:(Foo *)aFood
{
if (_foo != nil)
[_foo release];
if (aFood != nil)
_foo = [aFood retain];
}
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:
/**
AClass is an immutable container:
- category methods must never change the state of AClass
*/
#interface AClass : NSObject < NSCopying >
{
#private
NSString * title;
NSString * description;
}
/**
subclassing notes:
- do not override properties: title, description
- implement #protocol NSCopying
*/
/*
1) document copy on entry here, even though the compiler has no
additional work to do.
2) nonatomic in this case - these ivars initialized and never mutate.
3) readonly because they are readonly
*/
#property (copy, readonly, nonatomic) NSString * title;
#property (copy, readonly, nonatomic) NSString * description;
/* prohibited: */
- (id)init;
/* designated initializer */
- (id)initWithTitle:(NSString *)inTitle description:(NSString *)inDescription;
#end
#implementation AClass
#synthesize title;
#synthesize description;
- (id)init
{
assert(0 && "use the designated initializer");
self = [super init];
[self release];
return 0;
}
- (id)initWithTitle:(NSString *)inTitle description:(NSString *)inDescription
{
self = [super init];
assert(self && "uh oh, NSObject returned 0");
if (0 != self) {
if (0 == inTitle || 0 == inDescription) {
assert(inTitle && inDescription && "AClass: invalid argument");
[self release];
return 0;
}
/* this would catch a zombie, if you were given one */
title = [inTitle copy];
description = [inDescription copy];
if (0 == title || 0 == description) {
assert(title && description && "string failed to copy");
[self release];
return 0;
}
}
return self;
}
- (void)dealloc
{
/* which could also happen when if your init fails, but the assertion in init will be hit first */
assert(title && description && "my ivars are not meant to be modified");
[title release], title = 0;
[description release], description = 0;
/* don't forget to call through super at the end */
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
assert(self.title == title && self.description == description && "the subclasser should not override the accessors");
if ([self zone] == zone && [self class] == [AClass class]) {
/*
this is one possible (optional) optimization:
- avoid using this approach if you don't entirely understand
all the outlined concepts of immutable containers and low
level memory management in Cocoa and just use the
implementation in 'else'
*/
return [self retain];
}
else {
return [[[self class] allocWithZone:zone] initWithTitle:self.title description:self.description];
}
}
#end
Beyond that, avoid overusing autorelease calls 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:
Justin Galzic: so basically, the copy
ensures that objects are local to the
caller and when the instance is shared
out to the thread on which
NSOperations is on, they're two
different instances?
actually, the copy call to an immutable string could perform a retain.
as an example: AClass could now implement #protocol NSCopying by simply retaining the 2 strings. also, if you know AClass is never subclassed, you could just return [self retain] when the objects are allocated in the same NSZone.
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 what title and description point to in the implementation of AClass - this would break the policy you've established. if you wanted to change what the members of AClass pointed to, you'd have to use locks, #synchronized directives (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 copy is not guaranteed to create a new allocation - it just allows several guarantees to propagate to clients, while avoiding all thread safety (and locking/synchronizing).
What if there are some cases where you
do want multiple classes (on the same
thread) to share this object? Would
you then make an implicit copy of this
object and then pass along to the
NSOperation?
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 NSString which you know is immutable, you should just copy it from AClass.
if you want to share an instance of AClass you have 2 choices:
1) (best) implement #protocol NSCopying in AClass: - (id)copyWithZone: implementation added above.
now the client is free to copy and retain AClass as 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 use copy or retain as required. this is not realistic. it is a good way to introduce bugs if your implementation of AClass needs 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 retain and copy semantics 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 retain and 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 NSOperation subclass, 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 #protocol interface 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:
#protocol MONImageRenderCallbackProtocol
#required
/** ok, the operation succeeded */
- (void)imageRenderOperationSucceeded:(AClass *)inImageDescriptor image:(NSImage *)image;
#required
/** bummer. the image request failed. see the #a error */
- (void)imageRenderOperationFailed:(AClass *)inImageDescriptor withError:(NSError *)error;
#end
/* MONOperation: do not subclass, create one instance per render request */
#interface MONOperation : NSOperation
{
#private
AClass * imageDescriptor; /* never change outside initialization/dealloc */
NSObject<MONImageRenderCallbackProtocol>* callback; /* never change outside initialization/dealloc */
BOOL downloadSucceeded;
NSError * error;
}
/* designated initializer */
- (id)initWithImageDescriptor:(AClass *)inImageDescriptor callback:(NSObject<MONImageRenderCallbackProtocol>*)inCallback;
#end
#implementation MONOperation
- (id)initWithImageDescriptor:(AClass *)inImageDescriptor callback:(NSObject<MONImageRenderCallbackProtocol>*)inCallback
{
self = [super init];
assert(self);
if (0 != self) {
assert(inImageDescriptor);
imageDescriptor = [inImageDescriptor copy];
assert(inCallback);
callback = [inCallback retain];
downloadSucceeded = 0;
error = 0;
if (0 == imageDescriptor || 0 == callback) {
[self release];
return 0;
}
}
return self;
}
- (void)dealloc
{
[imageDescriptor release], imageDescriptor = 0;
[callback release], callback = 0;
[error release], error = 0;
[super dealloc];
}
/**
#return an newly rendered NSImage, created based on self.imageDescriptor
will set self.downloadSucceeded and self.error appropriately
*/
- (NSImage *)newImageFromImageDescriptor
{
NSImage * result = 0;
/* ... */
return result;
}
- (void)main
{
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSImage * image = [self newImageFromImageDescriptor];
if (downloadSucceeded) {
assert(image);
assert(0 == error);
[callback imageRenderOperationSucceeded:imageDescriptor image:image];
[image release], image = 0;
}
else {
assert(0 == image);
assert(error);
[callback imageRenderOperationFailed:imageDescriptor withError:error];
}
[pool release], pool = 0;
}
#end
If there is something I do not recommend, it is that you are keeping your reference unretained by the knowledge that it will be retained by someone else already. Because the reference is unretained, when it is removed from the array it will be released, retain count might drop to zero and object might be dealloc and now you are holding time bomb i.e. invalid reference.
I would suggest that do not autorelease the reference, and try to do break point at your dealloc and see the stack call to see who cause your object to be dealloc.
This is an object I made to do some flash cards. The first method (I left out the main part) generates a NSMutabaleArray of Card objects with the passed in operator and works fine. The second method, "drawFromDeck" gets called on a Deck object from my view controller and also works fine, but the Static Analyzer says I may be leaking an object.
Here is the code.
#import "Deck.h"
#class Deck;
#implementation Deck
#synthesize cards;
- (id)initDeckWithOperator: (NSString*)mathOper {
...
return self;
}
- (id)drawFromDeck {
int index = random() % [cards count];
Card* selectedCard = [[cards objectAtIndex:index] retain];
[cards removeObjectAtIndex:index];
return selectedCard;
}
#end
Yes you're leaking an object. You should
return [selectedCard autorelease];
The reason being you've -retained the selectedCard, so you've got the responsibility to -release it. But you can't use -release since it must be valid after the function ends, so you need to use -autorelease to transfer the ownership to the auto-release pool.
Of course, methods calling -drawFromDeck shouldn't -release its return value.
Let's say I'm building a new class for the iPhone in Objective-C. In one of my init methods I want to manually allocate some memory. So, I might have something like this:
- (id)initWithSomeObject:(SomeObject *)someObject {
self = [super init];
if (self != nil) {
myObject = someObject;
[myObject retain];
if ( (memory = calloc(1, sizeof(SomeStruct)) == NULL) {
// What should I do here to clean up
[self release];
self = nil;
}
}
return self;
}
Now, assuming that the calloc() could fail, and that failing to allocate memory is catastrophic for my object, what should I do inside the if-body to clean up properly? Is there an Objective-C idiom or pattern that I should be using?
Edit: I included the code posted by Rob Napier. But, I still have to release myObject, right? Or does the added code somehow trigger dealloc()?
Yes, you should release yourself and then return nil.
[self release];
self = nil;
See Issues with Initializers in the Concepts in Objective-C Programming guide.
You need to clean up anything you need to and then set the self reference to nil. Apple Dev Portal has an article:
Link
I just tried. -dealloc gets called due to [self release], so myObject would not need to get released in initWithSomeObject. To be sure, you might move myObject = [someObject retain]; (I prefer that style in case -retain might fail for some reason) below the call that might fail (if that's possible).
I'm implementing a singleton class as follows:
static Singleton* _singletonInstance;
#implementation Singleton
+(void)initialize
{
_singletonInstance = [[Singleton alloc] init];
}
+(Singleton*)instance
{
return(_singletonInstance);
}
initialize only gets called the first time someone calls instance. I then have a method that I can call to set up some instance variables. So it ends up looking like this.
_singleton = [Singleton instance];
[_singleton setupWithParams: blah];
When i get an instance of this singleton inside an object, it works fine the first time; However, after i dealloc and create a new copy of the object that needs an instance of the singleton, i get a BAD ACCESS error when I try to call the setup function.
Just to test things I print out the address of the instance before I make the setup call and both times they report the same address, but when i check the error log for BAD ACCESS call, it lists a completely different memory address.
Does anyone have any ideas why this pointer to the instance seems to look fine when I print it, but when I make a call to it, it is seemingly pointing to random data?
The pointer value looks valid because it used to be, but most likely the memory has been free'd, which is why what it points to looks like random data.
You've acquired one reference with your [[Singleton alloc] init] above, but is there a release somewhere else that might be executing? I bet your code is calling instance, and then release-ing later, even though your code never acquired a reference. And that shouldn't be necessary for a singleton anyways. Just a guess...
Are you deallocing your _singletonInstance somewhere?
I'm using much more complex, but very stable version of Singleton template (taken with description from Brandon "Quazie" Kwaselow Blog):
static SampleSingleton *sharedSampleSingletonDelegate = nil;
+ (SampleSingleton *)sharedInstance {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedSampleSingletonDelegate;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
sharedSampleSingletonDelegate = [super allocWithZone:zone];
// assignment and return on first allocation
return sharedSampleSingletonDelegate;
}
}
// on subsequent allocation attempts return nil
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
Valerii's code is better for implementing a singleton, but the problem is almost certainly that the code that calls [Singleton instance] is operating as if it has ownership without actually taking ownership using retain, and then is later releasing it.
Look there for your bug, and read the Memory Managment Rules.
Also, in Xcode, enable NSZombieEnabled and the console will show you when you try to message the object after its been released.