Bizarre object lifetimes in ARC: how to force precise object lifetimes? - objective-c

I am experiencing a bizarre behavior with object lifetimes in ARC. I have narrowed it down to this example:
//---------------------------------------------------------------------------------
#interface MyObject : NSObject
#end
#implementation MyObject
-(id)init
{
self = [super init];
if(!self) return nil;
NSLog(#" MyObject init %p", self);
return self;
}
-(void)dealloc
{
NSLog(#" MyObject dealloc %p", self);
}
#end
//---------------------------------------------------------------------------------
#implementation TLAppDelegate
-(MyObject *)createMyObject:(NSString *)unusedArg
{
return [MyObject new];
}
-(void)someOperation
{
NSLog(#" Entering someOperation");
MyObject* x = [self createMyObject:#"some message"];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"entering applicationDidFinishLaunching");
[self someOperation];
[self someOperation];
NSLog(#"exiting applicationDidFinishLaunching");
}
#end
Summary of code: applicationDidFinishLaunching calls someOperation twice in a row. someOperation creates a local object, which
I would expect is deallocated when someOperation returns.
Thus here is the output I would expect:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cd0
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
But here's the output I actually get:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0 <-- this object is retained until the end of the output!
Exiting someOperation; x should be deallocated right after this...
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
MyObject dealloc 0x600000012cd0
Why is the first object retained all the way until applicationDidFinishLaunching returns?
As far as I can tell, no MyObject instance should ever live outside of someOperation.
It's not a leak, because it's deallocated on the next scope.
It feels almost like the compiler inlines someOperation, merging scope with the caller. But this is also not true as
(correct me if I'm wrong), the compiler cannot inline objective-C methods.
For my real project, this is causing problems in multiple areas. First in our logging class we keep track of some scope, but because of this behavior, the following log:
Generating some list {
Calculating item #1 {
}
Calculating item #2 {
}
Calculating item #3 {
}
}
Turns into this, which is meaningless:
Generating some list {
Calculating item #1 {
Calculating item #2 {
Calculating item #3 {
}
}
}
}
Also, it means we are holding on to way too much memory for no good reason.
Is there a way to make this behavior more predictable?
Note that this behavior does not change if I use __attribute__((objc_precise_lifetime)); it makes no change to the observed behavior.

MyObject wouldn't be released at the end of someOperation because the object that createMyObject returns is retained (when created) and autoreleased (when returned).
So while someOperation subsequently assigns that object to x and retains and releases it as you'd expect, there's still that autoreleased reference from createMyObject that won't be cleared until the autorelease pool is drained (which normally happens at the end of every run loop).
If, instead of getting MyObject from createMyObject, you instantiated it directly like:
-(void)someOperation{
NSLog(#" Entering someOperation");
MyObject* x = [[MyObject alloc] init];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
there won't be that autoreleased reference hanging and everything should be deallocated immediately as you expect.
Update
Martin R brings up a good point about ARC and naming conventions. By default, an object returned by a method is retained/autoreleased by ARC (if it weren't retained, it'd be dealloc'd immediately. If it weren't autoreleased, it'd leak).
There are a handful of method that, according to Cocoa naming conventions, are expected to return a "retained" object — that is, an non-autoreleased object with a +1 retain count. For these specific methods, whose names start with alloc…, copy…, init…, mutableCopy…, or new…, ARC will return a retained object. Everything else returns an autoreleased one.

Related

In Objective-C under MRC if an object gets de-alloced do objects it created get de-allocated as well?

This may look like a dup of this, but I don't think that answers my question as it is about associated objects, not objects that were created by and whose only pointer resides within an object.
Let's say I had this example in MRC mode.
// In h file
#interface MyViewController : UIViewController {
NSObject* myNsObject;
}
// In m file
-(void) viewDidLoad() {
myNsObject = [[NSObject alloc] init]; // I'm never going to release myNsObject
}
I'm smart enough to release myViewController correctly. It's reference count goes to zero and it is de-allocated. But I never released myNsObject, so it had been hanging around with a reference count of 1. So would a release, and therefore de-alloc, automatically get done on myNsObject? Or would myNsObject get leaked in that case?
The proper memory management here is to release myNsObject in the dealloc method of the view controller:
- (void)dealloc {
[myNsObject release];
[super dealloc];
}
If you create something then you are responsible for releasing it (under MRC).
Failure to do this results in memory leaks.

memory/pointer behavior for self = [super init]

Forgiveness, please: I am a beginner. I was looking at another quesiton/answer and came across this code:
SpinningView *spinner = [[SpinningView alloc] initWithFrame:CGRectMake(0.0, 0.0, 20.0, 20.0)]
// Now let's take a look at the implementation of SpinningView's -initWithFrame: method
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
I believe that, in the second section of code, self points to the instance to which the message was sent that resulted in "self" being encountered, i.e., the result of [SpinningView alloc]. (Or doesn't that produce an instance?)
So, when you call self = [super initWithFrame:frame] on the 4th line of code, are you not reassigning the pointer value associated with "spinner"? I.e, are you not abandoning the memory you allocated in the first line? Or does the compiler someone know just to copy memory values instead of changing the pointer value? Or... what??
Thanks!
This is the standard idiom for the -init method of obj-c objects. The idea being that, whatever was allocated from +alloc doesn't matter, only what was returned from -init matters. Now, -init will usually just use the already-allocated object that's in self. But it's not required to. It is free to deallocate that object and create a new one. The classic example is when you alloc/init an NSString* you don't actually get back an instance of NSString*, you get back a concrete subclass. This is because NSString* is a "class cluster". So when you call +alloc you get back an NSString*, but when you call -init it frees that object and reallocates an object of one of its subclasses, initializes that new object, and hands it back to you.
Another example would be if you had a class that tried to memoize itself. Lets say you have an immutable class that gets initialized with a number. You could change your -init to re-use existing instances of the class. Here's an example (note: not thread-safe):
static NSDictionary *numberCache;
#interface MyNumber : NSObject
#property (readonly) int number;
- (id)initWithInt:(int)i;
#end
#implementation MyNumber
+ (void)initialize {
if (self == [MyNumber class]) {
numberCache = [[NSDictionary alloc] init];
}
}
- (id)initWithInt:(int)i {
// find ourself in the numberCache
NSValue *val = [numberCache objectForKey:#(i)];
if (val) {
// yep, we exist. Release the just-allocated object
[self release];
// and retain the memoized object and stuff it back in self
self = [[val nonretainedObjectValue] retain];
} else if ((self = [super init])) {
// nope, doesn't exist yet. Initialize ourself
_number = i;
// and stuff us into the cache
val = [NSValue valueWithNonretainedObject:self];
[numberCache setObject:val forKey:#(i)];
}
return self;
}
- (void)dealloc {
// remove us from the cache
[numberCache removeObjectForKey:#(_number)];
[super dealloc];
}
#end
#KevinBallard covered most of the points. The reason we need the self = is because init is not guaranteed to return the same object it is called on (it could return a different object or nil). I will answer your questions and expand on the memory management aspects:
I believe that, in the second section of code, self points to the
instance to which the message was sent that resulted in "self" being
encountered, i.e., the result of [SpinningView alloc].
Yes
So, when you call self = [super initWithFrame:frame] on the 4th line
of code, are you not reassigning the pointer value associated with
"spinner"?
Yes. Not spinner (spinner doesn't exist at this point anyway). You are re-assigning the pointer variableself inside the method.
I.e, are you not abandoning the memory you allocated in the first
line? Or does the compiler someone know just to copy memory values
instead of changing the pointer value? Or... what??
Yes. Under MRC, you are just re-assigning the pointer, and the compiler does not do anything except change the pointer value. Under ARC, it's more complicated, but at the end of the day, the compiler just does the same as under MRC in this case, i.e. just re-assigns the pointer.
It's not really "abandoning" the memory if you think about it. You see, by convention, init methods take ownership of ("consume") an already-retained object that they're called on (usually the return result of a call to alloc), and they return a retained object. But these two don't have to be the same object. So when your init method is called, its self is already retained, and the init method owns it, but then it calls [super init...], which calls the superclass's init method on self, so that method now takes ownership of the self which your init had ownership to. And in return, that superclass's init returns back to you a retained instance, which you assign to self. You did not "abandon" self because you gave it to the superclass's init method, which in turn became responsible for memory managing it (including releasing it if it wants to return something else).

objective-c broken pointer with callback woes

I'm having trouble with a broken pointer that's pointing to garbage after an object has been released. objectA is the delegate for a callback from another object, objectB.
objectA is being allocated and released quite often (in my application its a menu UI object). Every time objectA is being initialised it initialises objectB and begins an asynchronous operation and then calls back to objectA via the id delegate property.
How can I stop my pointer: id delegate from breaking ?
ObjA.m
-(void)dealloc{
[super dealloc];
}
+(id)init{
ObjA *objectA = [[ObjA alloc]init];
return objectA;
}
-(id)init{
if (self == [super init]){
ObjB *objectB = [ObjB initialiseWithDelegate:self];
}
return self;
}
-(void)callback:(id)arg{
//This callback is called from an asynchronous routine from objectB
}
-(void)terminate{
//This is called when I want to remove/release objectA
//Other logical termination code here
[self release];
}
ObjB.m
#synthesize delegate;
+(id)initialiseWithDelegate:(id)delegate{
ObjB *objectB = [[ObjB alloc]init];
[objectB setDelegate:delegate];
return objectB;
}
-(id)init{
if (self == [super init]){
[self beginAsynchronousOperation];
}
return self;
}
-(void)beginAsynchronousOperation{
//Do asynchronous stuff here
}
-(void)finishedAsynchronousOperation{
//Called when asynch operation is complete
if (self.delegate) [self.delegate callback:someargument]; //EXC error. Pointer broke
}
The short answer here is that you nil out objectB's delegate property when you dealloc objectA. Because delegates are assigned, and not retained (explicitly to prevent retain cycles), as you have seen, the delegate reference can be left hanging when the "owning" object goes away. Typically objectA will be holding a retained reference to objectB, and during objectA's dealloc, it will first set objectB's delegate to nil, and then release objectB. This will prevent the crash. Of course this assumes (as is typical) that you don't need to do anything with that async completion. It also assumes that objectB can safely be released by objectA when it is in the middle of an async operation. This is usually true of (say) animations, but if you're building your own tasks, you might need to be careful of the lifetimes here.
Some notes on this code snippet that might be helpful:
Your objectA isn't actually holding a reference to objectB once it's created. This means you can't nil out the delegate. You should keep the reference to objectB when it's created so you can do this.
You are leaking objectB. ObjectA creates it (alloc, init), but then drops the reference, so even when it's done, no one seems to be responsible for releasing it. Again, holding it so you can release it will fix this too.
Your -terminate on objectA is an anti-pattern-- an object should never (with only one exception: a failure inside init) be calling -release on self. An object should be released by its owner, which is whoever originally created it.
Your pattern of if (self == [super init]) is normally written with one equals sign, meaning that you're both assigning self to the result as well as checking it for nil. This is a Cocoa historical oddity, and probably makes no difference here, but worth pointing out.

I’ve set my property to retain, am I supposed to release it even if it is set to autorelease?

Let us say we have some code that looks like below:
#interface SomeClass : NSObject
#property (nonatomic, retain) NSString *someString;
#end
#implementation SomeClass
#synthesize someString;
-(id)init {
if (self=[super init]) {
someString = [NSString stringWithString:#"some string"];
}
return self;
}
#end
Am I supposed to release the someString property in the dealloc method of SomeClass, even if someString was set to autorelease and I never actually retained it in my init method? If so, I'd simply add [someString release] before [super dealloc] in the -release method. Correct?
Now the real issue I am having is that while using Cocos2D, I've reached a contradicting situation. My code looks like below:
#interface SomeLayer : CCLayer
#property (nonatomic, retain) CCSprite *someSprite;
#end
#implementation SomeLayer
#synthesize someSprite;
-(id)init {
if (self=[super init]) {
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"];
[self addChild:someSprite];
}
return self;
}
#end
Now, I have added someSprite as a child to my layer SomeLayer. So, what should I do to make sure I have no memory leaks here? I could think of the following:
Obviously, I'd think of calling [someSprite release] in SomeLayer's -dealloc method. but it gives me EXC_BAD_ACCESS in [super dealloc] (the next line). Most likely because I didn't remove the child, and the superclass tries to access the child that I just released.
I call [self removeChild:someSprite cleanup:YES] in the -dealloc method, which would remove the child and also release it. So I do not need to follow up with [someSprite release]. But hey, the -dealloc method of the superclass CCNode already does all that for me.
I do nothing. I wouldn't override the -dealloc method at all. Well, this seems to work fine, but it contradicts the statement: "if you retain something, you're supposed to release it".
Any help on why I must release the object in case I, and why not in case II at an early stage would help save a lot of memory related issues in the long run.
Thanks
someString = [NSString stringWithString:#"some string"];
This is wrong. You are keeping a pointer to an autoreleased object that will disappear soon, and when you’ll try to use the someString pointer bad things will happen. You should use the accessor ([self setSomeString:…]), retain the autoreleased value (someString = [… retain]) or use a method that returns a retained value (someString = [[NSString alloc] init…]).
In your real use case you should do the same with the sprite, you are getting EXC_BAD_ACCESS because you over-release the sprite: you call release without ever retaining the value. Read the Cocoa Memory Management Guide, you will save yourself a lot of trouble in the long run.
By the way, I think your main problem is that you think that a simple assignment to the someString variable retains the assigned value. That is not the case (not without ARC, to be more precise). Assignment to the instance variable is just that, a plain assignment. If you want to go through the accessors, you have to send a message ([self setSomeString:…]) or use the dot notation (self.someString = …).
You only have to release objects that you explicitly allocate. None of the examples you gave were allocated, so they are autoreleased. If you want to keep an autoreleased object for a long period of time, you need to retain it, and only then you would need to release the object.
Additionally, if you have properties you should set them to nil in viewDidUnload
self.someString = nil;
You really need to read Memory Management Programming Guide.
There are two of four rules
You can take ownership of an object using retain.
When you no longer need it, you must relinquish ownership of an object you own
When you declare property as retain then you should call release for appropriate variable. In your case your dealloc should looks
- (void)dealloc
[someSprite release];
[super dealloc];
}
And look at this code
if (self=[super init]) {
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"]; // here you assign pointer to new object
[self addChild:someSprite]; // all right, you can use newly created object in this scope
}
// but here that object can be deleted from memory and someSprite can points to nothing
To avoid this you need to retain newly created sprite
someSprite = [[CCSprite spriteWithFile:#"SomeFile.png"] retain];
#synthesize someSprite;
this line makes the retain count of SomeSprite to 1 ..in dealloc you release it so retain is back to 0.. object release.
[CCSprite spriteWithFile:#"SomeFile.png"];
this is an autorelease object..
when you do
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"];
you point someSprite to the autorelease object.. so both are now equal..
this line messes the whole point of synthesize(retain) ..so now change this line to
[self setsomeSprite] =[CCSprite spriteWithFile:#"SomeFile.png"];
now you just continue the way it was.. have someSprite release in the dealloc.. and everything will be good again

Autorelease then retain for setters

According to the Google Objective-C Style Guide, we should autorelease then retain as so:
- (void)setFoo:(GMFoo *)aFoo {
[foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo|
foo_ = [aFoo retain];
}
In this case, foo_ will not be deallocated if being set to the same instance, making for a more defensive setter.
My question is, is this how #property & #synthesize work?
release due to an autorelease isn't called until the end of the current runloop so foo_ wont dealloc because retain is called first followed by release at the end of the current runloop. However, this isn't how the code generated in #synthesize works. It works more like
- (void)setFoo:(GMFoo *)aFoo {
if (aFoo != foo_) {
[aFoo retain];
[foo_ release];
foo_ = aFoo;
}
}
This method saves cpu cycles when no change is necessary and takes out the small overhead of using the autorelease pool.