Reputation: 211
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
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
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