Instruments is reporting me a leak of a NSDate variable. But If I add up the retains and releases it should be able to release I think, by the autorelease pool. Probably I'm counting wrong but I wan't to make sure. Take a look at the RefCt.
If I [Class alloc] it should come up with a retain count of 1, then if I autorelease that object, it should be able to free, or is it not?
Instruments adds up the retains and releases for you. That's what the “RefCt” column shows you: The running total.
If I [Class alloc] it should come up with a retain count of 1, …
And indeed it does; that's the first row in the list.
… then if I autorelease that object, it should be able to free, or is it not?
Autorelease isn't an immediate -1; it causes a release later, and that's the -1.
So you have:
Allocation: +1 (=1)
Autorelease: 0 for now; causes a Release later (no change now, so still =1)
Retain: +1 (=2)
Release: -1 (=1)
Retain: +1 (=2)
Retain: +1 (=3)
Release: -1 (=2)
Release: -1 (=1)
Note that one of the three Releases is the one caused by the Autorelease. Only then is -1 incurred.
The object needs another release in order to be deallocated. Until that happens, it won't.
And yes, it is possible for an object that has enough outstanding autoreleases to kill it when they come due to be retained before that happens and thereby be kept alive. I saw this happen once with an object that I was under-retaining, but that was the value of a property being used by a Binding; the Binding retained the value and so kept it alive even after I had autoreleased my own last ownership of it.
Related
In one interview i was asked to implement NSArray's exchangeObjectAtIndex:withObjectAtIndex: method.
I wrote the following code:
- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 {
id tmp = [self objectAtIndex:index1];
[self replaceObjectAtIndex:index1 withObject:[self objectAtIndex:index2]];
[self replaceObjectAtIndex:index2 withObject:tmp];
}
Interviewer said here's a memory management problem in first line and I'm going to catch bad_access_exc.
He recommended to write as this:
- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 {
id tmp = [[[self objectAtIndex:index1] retain] autorelease];
[self replaceObjectAtIndex:index1 withObject:[self objectAtIndex:index2]];
[self replaceObjectAtIndex:index2 withObject:tmp];
}
I understand that his code is right, but since tmp is local variable and it's going to be assigned, so there's no releasing and everything is gonna be ok. Is there any error?
If you are using manual memory management, there is an error. Apple has documented the problem under “Avoid Causing Deallocation of Objects You’re Using” in the Advanced Memory Management Programming Guide.
Specifically, objectAtIndex: doesn't retain and autorelease the object that it returns to you. So the NSArray might have the only “owning” reference to the object. Assigning to tmp under manual retain counting (MRC) doesn't retain the object so tmp doesn't own it and the autorelease pool doesn't own it.
This means that when line 2 of your method sends [self replaceObjectAtIndex:index1 withObject:[self objectAtIndex:index2]], the array might release the last reference to the object, deallocating it. At that point, tmp refers to a deallocated object; this is called a “dangling reference”.
Then in line 3, you try to put the dangling reference in the array. The array will send retain to the reference, which is invalid, and you will crash or experience heap corruption.
Under ARC, assigning to tmp does retain the object, so there is no error in that case.
Remember that id tmp is nothing more than a pointer to the object in your array. It doesn't say anything about the memory management of the object it's pointing to.
...it's going to be assigned, so there's no releasing...
This is the sticking point here. You can't guarantee that the object at index1 won't be deallocated when you replace it with the object at index2. In fact, the array will call release on it at this point to balance out the retain it called on the object when it was originally added to the array. Thus, it's possible that when the object at index1 is replaced will the object at index2, the reference count of the object at index1 will go to zero, the object will be deallocated, and your tmp variable will turn into a dangling pointer. The ... retain] autorelease] dance keeps the object around long enough to do the swap without having to worry about it deallocating before the end of the method (likely it will stick around until the top of the next run loop).
I know that alloc and retain will increase the reference count of an object. Is there any other different method that actually increment the reference count? And when/how does dealloc is called?
alloc allocates an object with retain count 1.
Methods that start with new also return an object with retain count 1.
retain increments the count by 1.
release and autorelease (at the end of the run loop) decrement it by 1.
Methods that start with the name of the class (without prefix) return an autoreleased object, meaning that it will be released at the end of the cycle, if you don't retain it yourself.
Finally, methods that copy an object (usually start with copy) also create a copy with retain count 1.
dealloc is called when the retain count of an object drops to 0.
PS. In case you didn't know about it yet, consider using Automatic Reference Counting (ARC).
With these the retain count gets increased.
new, however it can be seen as alloc+init.
retain
copy creates new object with retain count=1
mutableCopy creates new object with retain count=1
dealloc is called automatically as soon as retain count reaches to 0.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
myClass *obj = [[myClass alloc] init];
NSArray *ar = [NSArray array];
[ar addObject: obj];
[ar removeObject: obj];
[pool drain];
Will removing an object from an NSArray array automatically release its memory that I have earlier allocated?? The answer seems to be yes from what I have found from various sources. The problem is if I test for memory leaks, xcode still complains that obj has not been released. So what's actually going on?
Collections retain the objects you add to them, claiming temporary ownership. When you remove an item from the collection, it releases the object (and its temporary claim). In other words, the retain count will be the same before you add an object to a collection and after you remove it.
If that retain count is 0, the memory is reclaimed.
In your code you're allocating an object and claiming ownership of it. That means it has a retain count of 1.
Then you're adding it to the array. The array retains the object, taking temporary ownership and upping its retain count to 2.
You then remove the object from the array. The array releases the object and relinquishes any claim of ownership, bringing the retain count back down to 1.
Since memory is not reclaimed until retain count is back to 0 (nobody has a claim on the object), your object's memory is not reclaimed.
If you had autoreleased the object prior to adding it to the array, or called release on the object after you had removed it (but not both!), the retain count would be 0 and the memory would be reclaimed.
Yes. When you insert an object into an array, the array retains it (bumps its retain count). If the object's retain count is 1 (ie, there are no other retains on it) then when it's removed from the array the retain count goes to zero and it's eligible to be deleted.
But your problem in the above scenario is that, after adding the object to the array, you failed to release YOUR retain on the object (due to the alloc/init). Insert [obj release] after the [ar addObject:obj].
(Also note that in your example the entire array will go "poof" when you drain your autorelease pool.)
No, you alloc it -> retain count of 1
You add it to the array which sends the object another retain -> 2
You remove the object from the array and the array sends a release -> 1
...so now the retain count is back to 1, which is your initial alloc retain, so you need to release it to free the memory.
Scenario1:
NSDictionary *dictionary =
[[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.stateZips = dictionary;
[dictionary release];
Scenario2:
self.stateZips = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
dependes on stateZips property.
If it is retained:
Scenario 1: stateZips is properly retained ( a release on stateZips will call its dealloc). also local dictionary is released then and there.
Scenario 2: stateZips is retained twice ( a release in stateZips will not call its dealloc as it is still retained).
If it is assigned:
Scenario 1: stateZips points to released dictionary and accessing it else where might result in crash.
Scenario 2: stateZips is properly retained ( a release on stateZips will call its dealloc).
copy is not being considered, as i believe its not your intention (at least in this piece of code)
Both cause self.stateZips to be set to a dictionary initialized with the file pointed to in plistPath.
But in the second, the pointer to the initialized dictionary was not saved, and as it's an object with a retain count of +1 technically a release message needs to be sent to it in some place, to balance the memory management. But as there is no way to retrieve the pointer to that object, you'll end up with a memory leak.
Two exceptions apply:
1.Garbage Collection
If you're in a garbage collected environment, both are the same. Well, they are not the same, but the result is similar.
2.Property type
If the setter for stateZips simply assigns the pointer, then you can release the object using the ivar pointer. Then these two pieces of code have only one difference: in the former, the object is released right after it's used. In the latter, it's just "undefined". Without the context, it's hard to determine if this object was released or not, and when.
I am assuming that stateZips is a property with the retain attribute.
In Scenario 1. A dictionary is created with a retain count of 1 in the first line. In the second line the property will call retain again, increasing the retain count to 2. Finally the retain count is decremented by the release. This will leave the dictionary with the correct retain count.
In Scenario 2, the retain is only called once.
The net effect of the two scenarios is the same. The dictionary object will be retained, and you will need to include a release in the dealloc method of the class.
If this were not correctly handled by the compiler, it would be very hard indeed following the retain/release rules of objective-c.
When I create an object and check its retain count, I get 1 as expected. When I release the object and then check the retain count again, it is still 1. Shouldn't the object be deallocated, and the retain count 0?
NSMutableString *str=[[NSMutableString alloc] initWithString:#"hello"];
NSLog(#"reference count is %i",[str retainCount]);
[str release];
NSLog(#"reference count is %i",[str retainCount]);
I do see 0 for the retain count if I set str to nil first. Why is that?
Don't use retainCount, it doesn't do what you expect in most cases.
Your second NSLog is accessing deallocated memory as an object. In this particular case, that deallocated memory still contains enough of the old data from the NSString that was just freed for the program to not crash when the retainCount method is called on it. Had you run this with NSZombieEnabled you would have gotten an error message about sending a message to a deallocated instance.
The reason it returns 0 when called for nil is that methods returning integers will always return 0 when called on a nil object.
Do not depend on retainCount. And do not care about this. Lots of things may happen under the hood. You only need to ensure that you have released all the things that you owned. If you are trying to be sure that you are not leaking any memory, then use Instrument, not retainCount in NSLog.