Bharath Booshan
Bharath Booshan

Reputation: 211

Strange behavior in Objective-C object dealloc in ARC enabled Environment

Problem : I am seeing some strange behavior in the order in which dealloc method is invoked on a static object after being set to nil.

To explain the problem, I have taken a class name BBSampleClass, which has two methods

interface BBSampleClass : NSObject
+ (BBDummyObject*)getInstance; 
+ (void)cleanUp;
@end


static BBSampleClass *sInstance = nil;
@implementation BBSampleClass

+ (void)initialize
{
    sInstance = [[BBDummyObject alloc] init];
}

+ (BBDummyObject*)getInstance
{
    return sInstance;
}

+ (void)cleanUp
{
    sInstance = nil;
}

- (void)dealloc
{
    NSLog(@"BBSampleClass Object Dealloc");
}

@end

At some point in my application, If I invoke class method cleanUp in BBSampleClass, (which sets the static instance variable sInstance to nil), one would expect its dealloc be called immediately before executing other statements, as no other object owns sInstance at that moment.

i.e. executing these two statements

[BBSampleClass cleanUp]
NSLog(@"After Cleanup");

Should output following to console, which is Correct.

**2013-01-11 14:14:32.280 BBSampleCode[7781:c07] BBSampleClass Object Dealloc

2013-01-11 14:14:32.280 BBSampleCode[7781:c07] After Cleanup**

However, If I try to retrieve the BBSampleClass's object through its getInstance class method, like this

[BBSampleClass getInstance];//do something with object 
[BBSampleClass cleanUp];  
NSLog(@"After Cleanup");

The order of execution of NSLog statements is reversed, i.e. BBSampleClass's static object is deallocated after executing the statement NSLog(@"After Cleanup") , which is wrong.

 **2013-01-11 14:15:43.940 BBWebView[7811:c07] After Cleanup
2013-01-11 14:15:43.940 BBWebView[7811:c07] BBSampleClass Object Dealloc**

EDIT: Solution

Moving [BBSampleClass getInstance] into an @autoreleasepool { } block solves the issue.

Upvotes: 1

Views: 265

Answers (2)

Martin R
Martin R

Reputation: 539685

initialize is sent to a class before any other message, so you start with a strong reference sTop to your object.

Scenario #1:

[BBDummyObject cleanUp] sets sTop = nil. The last strong reference to the object is gone and the object is deallocated immediately.

So "Dummy Dealloc" is printed before "After Cleanup".

Scenario #2:

The [BBDummyObject getInstance] method retains the returned object sTop and adds it to the autorelease pool, as described in 3.2.3 Unretained return values of the Clang ARC documentation.

[BBDummyObject cleanUp] sets sTop = nil. This destroys the strong reference from sTop to the object and the retain count is decremented. But the retain count is still positive, therefore the object is not destroyed at this point.

Then you print "After Cleanup".

When the program leaves the scope of the current @autoreleasepool { }, all objects in the autorelease pool receive a release message. The retain count of your object is now zero and the object is released.

Therefore "Dummy Dealloc" is printed now.

A simplified description is: [BBDummyObject getInstance] creates a strong reference which is destroyed automatically when the current autorelease pool is destroyed. Therefore [BBDummyObject cleanUp] does not destroy the object immediately, and "After Cleanup" is printed before "Dummy Dealloc".


getInstance returns an unretained return value due to the ARC naming conventions. If you rename your method to copyInstance then it would return a (+1) retained value, which is not added to the autorelease pool. For Example:

BBDummyObject *obj = [BBDummyObject copyInstance]; // retain count of object is increased.
// do something with obj ...
obj = nil; // retain count is decreased immediately.
[BBDummyObject cleanUp]; // should dealloc object immediately.

Upvotes: 2

Anoop Vaidya
Anoop Vaidya

Reputation: 46533

Scenario #1

[BBDummyObject cleanUp];
NSLog(@"After Cleanup");

Output #1

 Dummy Dealloc
 After Cleanup

This is somewhat straight :

You called cleanUp, which is setting it to nil. Which results to call dealloc.Then your NSLog worked.

Upvotes: 1

Related Questions