Reputation: 1783
I was reading the apple documentation for memory management, and came across something that I just don't understand. Basically, I don't understand why one does not need need to retain an instance variable through the "getter" method. I wrote this little program to see what would happen. I thought there would be a crash, but I am obviously missing something.
// main.m
// Test
//
#import <Foundation/Foundation.h>
#import "Test.h"
int main(int argc, char *argv[])
{
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
//Initialize the test object
Test *t = [[Test alloc] init];
//Set the value to 5
[t setMyNum:[NSNumber numberWithInt:5]];
//Save a temp number that points to the original number
NSNumber *tempNum = [t myNum];
//release old number and retain new
[t setMyNum:[NSNumber numberWithInt:7]];
//Shouldn't this crash because tempNum is pointing to a deallocated NSNumber???
NSLog(@"the number is %@",tempNum);
[p drain];
return 0;
}
Doesn't tempNum point to a deallocated object??
All help is appreciated.
EDITThis is the code in the getter and setter methods
#import "Test.h"
@implementation Test
- (void)setMyNum:(NSNumber *)newNum {
[newNum retain];
[myNum release];
myNum = newNum;
}
-(NSNumber *)myNum {
return myNum;
}
@end
As you can see I am calling release on the old object.
EDITIt was suggested, and I thought rightfully so that the reason the tempNum is still around is because it hadn't been autoreleased from the pool yet. But even after moving the [pool drain] to right before the NSLog message, there is not crash??? Weird.
Upvotes: 1
Views: 954
Reputation: 11
#import "Test.h"
@implementation Test
(void)setMyNum:(NSNumber *)newNum
{
[newNum retain];
[myNum release];
myNum = newNum;
}
(NSNumber *)myNum
{
return myNum;
}
@end
Here is the setter method [myNum release];
Which releases the myNum
, but then we are again giving some new value that is newNum
, hence from the getter method the temporary number gets the number which has not been deallocated till the [p drain];
so there will not be any crash. Even if the following code will not crash as there is autorelease pool but no autorelease method.
[t setMyNum:[NSNumber numberWithInt:70]];
So releasing the pool will not deallocate the number.
Upvotes: 0
Reputation: 11
#import "Test.h"
@implementation Test
- (void)setMyNum:(NSNumber *)newNum
{
[newNum retain];
[myNum release];
myNum = newNum;
}
-(NSNumber *)myNum
{
return myNum;
}
@end
Here in the setter method [myNum release]
which releases the mynum, but then we are again giving some new value that is newnum, hence from the getter method the temporary number gets the number which has not been deallocated till the [p drain]
so there will not be any crash.
Upvotes: 1
Reputation: 28600
Since you are not explicitly releasing any objects, nothing is being deallocated until the autorelease pool is allowed to drain. Try inserting [p drain]
before the last NSLog
call. It should crash the NSLog call.
Additionally, if you are not retaining the NSNumber in your setMyNum: method, you will find that it will crash if you add [p drain]
before tempNum is assigned.
To clarify the original question, calling a getter method doesn't (and shouldn't) necessarily imply that the caller wants to take ownership (i.e. retain) the variable. If that was the case, this code would leak:
NSLog("Number is %@", [t myNum]);
Also, it appears that NSNumber has an optimization whereby for small numbers, they cache the NSNumber objects, retain an extra copy, and return that version. So for small constants, [NSNumber numberWithInt: N]
will return an object with 2 reference counts (available via [theNumber retainCount]
). To explicitly see what happens, use a larger constant in the program, an NSNumber will retain a 'fresh' object with a reference count of 1 (that will also be autoreleased).
Upvotes: 5