iPhone memory management(Specially for property)
I have a very clear question:
//.h file
@property (nonatomic, retain)NSMutableString * retainString;
@property (nonatomic, copy)NSMutableString * copyString;
//.m file
@synthesis retainString, copyString;
-(void)Process
{
NSMutableString *test = [[NSMutableString alloc]inti];//retain count should be 1
self.retainString = test;
self.copyString = test;
}
cond. 1-> // retain count of both should be 2. As they are pointing to the same memory location with retain count 2 so how may release should be written.
cond. 2-> // //retain count of test is 1 and copyString is 2. As both hold different memory location. but can we write [copySt开发者_如何学编程ring release].
This setup actually does some very interesting things, and raises a couple good points about Objective-C memory management. Let's first reiterate the code:
// Testing.h
@interface Testing : NSObject {
NSMutableString *retainString;
NSMutableString *copyString;
}
@property(nonatomic,retain) NSMutableString *retainString;
@property(nonatomic,copy) NSMutableString *copyString;
// Testing.m
@implementation Testing
@synthesize retainString, copyString;
- (id)init {
if(self = [super init]) {
NSMutableString *test = [[NSMutableString alloc] init];
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.retainString = test;
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.copyString = test;
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
[self.copyString appendFormat:@"test"];
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
}
return self;
}
@end
This produces the log output:
2009-12-24 03:35:01.408 RetainCountTesting[1429:40b] test 1; retain 0; copy 0 2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 0 2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 2147483647 2009-12-24 03:35:01.413 RetainCountTesting[1429:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'
So what's going on here? The first two calls are fairly straightforward:
- The initial call to
alloc
/init
creates a new NSMutableString object with retain count 1, as expected. We have one object with one retain on it. - The assignment to the
retain
ed property increments the retain count, as expected. We have one object with two retains on it.
Here's where it gets strange. The assignment to the copy
property does indeed make a copy, but not in the way you'd expect. NSString and NSMutableString are part of what's called a class cluster - when you create or modify a string, it may or may not be an instance of the class you're expecting. The language may mutate it to some other representation behind the scenes.
In this particular case, when the copy is performed, apparently the language decides that the string (since it contains no information) is to be considered immutable, and makes it so. This is often seen when people do something like [[NSString alloc] initWithString:@"hello"]
- it's a constant, static string, so no object need be allocated dynamically. Keeping it static helps the runtime perform better.
So now we have two objects: our original test
object that was retained twice, and the new object that is static and therefore has a retain count of INT_MAX
. Finally, since the new string is immutable, calling a mutator method on it kills the program.
As an aside, changing the original call from init
to initWithString:
does make the copy assignment perform (somewhat) as expected - you only get a retain count of 1 on the copied object, but you still can't mutate it. Again, this is probably due to some optimization magic inside the compiler that decided that string was static and saw no reason to make it mutable if it didn't have to.
To answer your final question: yes, you can call release
on either of these objects. It just won't do much. At best, you'll have destroyed the copied object (since it had a retain count of 1); at worst, it'll do nothing to a static string object. However, I'd recommend continuing to work through properties: rather than releasing the copied object, why not just do self.copyString = nil;
? Since it calls the property setter, it'll take care of the release as necessary, and then you don't have a pointer to the object still floating around.
For more information on all this, consider reading:
- Memory Management Programming Guide for Cocoa
- The NSString class reference
- The Objective-C Programming Guide, Declared Properties section
If you have properties defined with keywords 'retain' or 'copy' you should always release corresponding member variables in the dealloc method.
In this case your dealloc should look like this:
- (void)dealloc {
[retainString release];
[copyString release];
[super dealloc];
}
Now, when you alloc
a string in your custom class' method, this method owns the string as described in the Memory Management Programming Guide for Cocoa. This means that you should release
the string before leaving the method.
@synthesize retainString, copyString;
- (void)Process {
NSMutableString *test = [[NSMutableString alloc] init]; //retain count is 1
self.retainString = test; // retain count of test is 2
self.copyString = test; // test's retain count = 2, copyString's = 1
[test release]; // retain count of test is 1 again
}
When an instance of this class is destroyed, the dealloc
method will be invoked which will in turn release
both strings. And as soon as their have retain counts remain 1, they will be dealloc'ed
too.
This may also help NSString property: copy or retain?
精彩评论