Reputation: 14164
I realize this question may sound dumb, but just bear with me. I built an app to help new developers wrap their head around memory retention on the iPhone (no ARC yet). It is plain and simple, 4 buttons, init, access, retain, and release. Pretty self explanatory. I am displaying what the retain count for my string object that is the target of our poking and prodding. (Please no lectures on use of [myVar retainCount], I already know)
This stuff will never make it into actual apps, just toying with it for fun and hopefully help someone learn how memory works. My retain and release all work great. My question is that why does my retain count drop back to 1 if I call myString = [[NSMutableString alloc] init]; again. I can boost my retain count to 40, but after calling alloc/init I go back to zero. I am not leaking anywhere, just curious what happens to myString if/when alloc/init is called on it again.
Upvotes: 2
Views: 1424
Reputation: 162722
My question is that why does my retain count drop back to 1 if I call
myString = [[NSMutableString alloc] init];
again?
Because you are failing to understand a very basic concept of Objective-C; myString
is not an instance of an NSMutableString
, but a reference to an instance. If you were to:
myString = [[NSMutableString alloc] init];
myString = [[NSMutableString alloc] init];
You now have two instances of NSMutableString, one leaked.
If you:
myString = [[NSMutableString alloc] init];
otherString = myString;
You now have a single instance of NSMutableString with two references.
In all three allocations, the NSMutableString instance will have a +1 retain count and, thus, you must balance each with a single release
or you'll leak.
Treating retain counts as an absolute count is a path to madness. Or, at best, the scope of usefulness of the absolute retain count is so limited that learning about it is not applicable to real world iOS programming.
This bears repeating:
The retainCount
of an object is tricky business.
If you were to continue down this path, you should be aware of the following details:
retainCount
can never return 0retain
/release
/autorelease
won't work as some classes don't actually use those methods to maintain the retain count (implementation detail, changes per platform/release, etc..)If you are going to teach retain/release, you should be treating the retain count as a delta and focus entirely on "If you increase the RC, you must decrease it".
Upvotes: 27
Reputation: 9132
Try this.
NSString *myString = [[NSMutableString alloc] init];
NSLog(@"%d", [myString retainCount]); // "1"
for (int i = 1; i < 40; i++)
[myString retain];
NSLog(@"%d", [myString retainCount]); // "40"
NSString *backup = myString;
myString = [[NSMutableString alloc] init];
NSLog(@"%d", [myString retainCount]); // "1"
NSLog(@"%d", [backup retainCount]); // "40"
You see, you have a different object with a new retain count. Your original object still exists and still has the same retain count. Assignment changes the object a variable refers to. A variable doesn't have a retain count, an object does.
myString = someOtherString;
NSLog(@"%d", [myString retainCount]); // who knows?
With retained property:
self.iString = backup;
NSLog(@"%d", [self.iString retainCount]); // "41" - 1 more because property retained
NSString *newString = [[NSMutableString alloc] init];
NSLog(@"%d", [newString retainCount]); // "1"
self.iString = newString;
// 1 for alloc 1 for retain (in real code you should release newString next)
NSLog(@"%d", [self.iString retainCount]); // "2"
NSLog(@"%d", [backup retainCount]); // "40" - self.iString released it
Upvotes: 2
Reputation: 3380
when you call myString = [[NSMutableString alloc] init];
, you're not "calling alloc/init on it again". You're not calling a method on the same instance you had. You're allocating and initializing a new instance, a completely different object from the one you had before.
And if you're doing that with a variable that had an object that you retained, then yes, you are leaking it.
Upvotes: 11