Task C Confusion Memory Management
I was reading Apple's documentation for memory management and came across something I just don't understand. Basically, I don’t understand why there is no need to store the instance variable through the "getter" method. I wrote this little program to see what happens. I thought there was going to be a wreck, but I obviously missed 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;
}
Does tempNum point to a deallocated object?
All help is greatly appreciated.
EDITThis is the code in 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 the release on the old object.
EDITIt was suggested and I thought that the reason tempNum still exists is because it hasn't been auto-implemented from the pool yet. But even after moving the [drain pool] right before the NSLog message, does it fail ??? Weird.
a source to share
Since you are not explicitly freeing any objects, nothing is released until the autoresource pool is removed. Try to insert [p drain]
before the last call NSLog
. This should cause the NSLog call to fail.
Also, if you don't store NSNumber in your setMyNum: method, you will find that it will fire if you add [p drain]
tempNum before the assignment.
To clarify the original question, calling a getter method does not necessarily (and does not necessarily) mean that the caller wants to own (i.e. store) the variable. If so, this code will flow:
NSLog("Number is %@", [t myNum]);
Also, it looks like NSNumber has an optimization where, for small numbers, they cache NSNumber objects, keep an extra copy, and return that version. Therefore, for small constants, it [NSNumber numberWithInt: N]
will return an object with two reference counts (accessible via [theNumber retainCount]
). To see clearly what will happen, use a large constant in your program, NSNumber will keep the "fresh" object with reference number 1 (which will also be auto-implemented).
a source to share
#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]
that frees mynum, but then we give a new value to newnum again, hence from the getter method the temp number gets a number that was not freed before [p drain]
so there will be no crash.
a source to share
#import "Test.h"
@implementation Test
(void)setMyNum:(NSNumber *)newNum
{
[newNum retain];
[myNum release];
myNum = newNum;
}
(NSNumber *)myNum
{
return myNum;
}
@end
Here is a setter method [myNum release];
that frees myNum
, but then we give a new value again newNum
, so from the getter method, the temp number gets a number that hasn't been freed before [p drain];
, so there won't be any crash. Even if the following code does not fail, as there is an autodetect pool, but no autodetect method.
[t setMyNum:[NSNumber numberWithInt:70]];
Thus, freeing the pool will not free the number.
a source to share