When Objective C containers are dealloc'd, do they release their references to the objects they contain or do I need to do that manually?
Should have read the docs for NSArray closer:
Arrays maintain strong references to their contents—in a managed memory environment, each object receives a retain message before its id is added to the array and a release message when it is removed from the array or when the array is deallocated. If you want a collection with different object ownership semantics, consider using CFArray Reference, NSPointerArray, or NSHashTable instead.
They release their references to the objects that they contain.
When you add an object its reference count is incremented. When it's removed (wither manually or when the array is destroyed) its reference count is decremented.
So with the following code you would not have to release the object
NSObject* someObject = [[[SomeClass alloc] init] autorelease];
[someArray addObject: someObject];
When an array is deallocated, each element is sent a release message.
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 have similar question to this: What is the difference between setting object = nil and [object release] VS [object release] and object = nil?
NSMutableArray *myExampleArray = [[NSMutableArray alloc] init];
myExampleArray = nil;
I use iOS 5.0 automatic reference counting, so in fact I don't release any objects. So if I assign it to nil is it equal to [myExampleArray release] ?
I know that i can't later use myExampleArray without re-initial it. So next question. What is the best way to clear this NSArray?
Yes, in an ARC environment you never call release. So assigning nil to the variable will release the object.
In a non ARC environment, you would do a release on your own, so the object gets destroyed. But the variable would still point to the old object adress. But there is no object anymore, so you would probably get a crash (EXC_BAD_ACCESS), if you use the variable later. If you also assign nil to it, that won't happen. Because the variable won't point to the old object address anymore.
Your other question:
If you need the array later again, you can call removeAllobjects on a NSMutableArray to remove all added objects, like Ankit Gupta said already. This will result in an empty array, that is still alive, so you can reuse it.
Don't use Nil for your object
try this line:
[myExampleArray removeAllobjects];
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.
How exactly does the addObject method of NSMutableArray work? Does it create a new instance and add it into the array or does it simply add a reference to the SAME object into the array?
If the answer is it only insert a reference to the object, then it leads to my next question:
Let's say I have the following method in one of my class ('list' is a NSMutableArray), gladly, this code works the way I wanted, but i just don't seem to fully understand why:
-(void)buyItem:(Item *)anItem
{
Item * newItem = [[Item alloc]init];
newItem.name = anItem.name;
newItem.details = anItem.details;
[list addObject:newItem];
[newItem release];
}
So basically after calling [list addObject:newItem], there would now be total of two reference pointing to the same object right(newItem, and another one in the 'list' array)?
But why does releasing the newItem object here, doesn't wipe out the one in the 'list' NSMutableArray? Aren't they pointing to the same Object?
When you are adding object to NSMutableArray using method addObject: it retains added object. This is why you can release it later and use afterwards by accessing using objectAtIndex: method.
It adds a reference and then increases the objects retain count by one. What you are doing is correct and it will still exist in the array with a retain count of one.
For your reference.
What increases an object's retain count?
It's important to understand the distinction between release and dealloc. release simply decrements the "retain count", except that when the count is decremented to zero, release goes on to dealloc the object.
In general (except where documented otherwise), when you pass an object reference (ie, pointer) to an Objective-C object, and it keeps a copy of that reference beyond the duration of your call to it, it retains the object on its own behalf, and it takes the responsibility to release the object when it is itself deallocated, or when the copy of the reference is nullified or overwritten.
If I have this:
NSString *lastPushed = (NSString *)[tagStack objectAtIndex:[tagStack count]-1];
.//do something with the last pushed element locally
.//do more cstuff here
.//after doing all the stuff needed in the function
[lastPushed release];
where tagStack is an NSMutableArray
If I release lastPushed, since it was not copied, or inited, will it only release this reference or will it actually release the object in the mutableArray?
You didn't alloc, new, or copy or mutableCopy lastPushed, so why are you releasing it?
Since lastPushed is a pointer to the object in the array, you are releasing that. This could lead to a problem when your array thinks that it has a valid pointer to an object but you've released it and it's been dealloced.
It will release the object in the array because both lastPushed and the array point to the same object.
As always, you should follow the management rules. Do not release anything that you have not created, retained or copied.
You shouldn't be releasing lastPushed, as you didn't use alloc, copy or new. (It's that simple.)
If you want to remove it from the mutable array, you should use the appropriate method in the NSMutableArray (removeObject or removeObjectAtIndex, etc.) Otherwise, it's unclear what you're trying to do which will bite you next week/month/year, etc.