I have a loop where I'd like to redefine the contents of an array with every iteration. I'm worried about leaks. Is there anything wrong with this code?
for (int i=0;i<numberOfReps;i++){
NSArray *shuffledArray=[self shuffleArray:originalArray];
// use shuffled array
}
Thanks for reading!
EDIT:
Here is shuffleArray (credit to Kristopher Johnson from What's the Best Way to Shuffle an NSMutableArray?):
-(NSArray*)shuffleArray:(NSArray*)array{
NSMutableArray *newArray=[NSMutableArray arrayWithArray:array];
NSUInteger count = [newArray count];
for (NSUInteger i = 0; i < count; ++i) {
// Select a random element between i and end of array to swap with.
int nElements = count - i;
int n = (arc4random() % nElements) + i;
[newArray exchangeObjectAtIndex:i withObjectAtIndex:n];
}
return [NSArray arrayWithArray:newArray];
}
(And I am using ARC.)
Assuming we're using ARC and the shuffleArray method is fine, this bit of code will be fine too. There's no leak in the code you've posted.
Each shuffledArray is deallocated at the end of each iteration of the loop (unless you're saving it somewhere else). And there is only one originalArray.
EDIT: After the edit, again, assuming we're using ARC, there's no leak with the code. newArray is released as soon as shuffleArray returns. shuffledArray is released at the end of the loop iteration. originalArray has reference outside the for loop, and remains in memory until it has no more references.
The only possible leak here would be whatever you do with shuffledArray in the //use shuffled array section of the loop.
That depends.
Only if you're sure that the method shuffleArray returns an autoreleased object then there's no harm with your code, (and this assuming you're not using ARC).
EDIT:
After seeing your code update, regardless if you're using ARC, there's no leak
Related
What can cause an assignment or change to an NSMutableArray to crash?
I have a mutable array containing custom objects, and I consistently keep no more than the 3 latest objects in it, the rest are removed. I started calling
[myArray insertObject:newObject atIndex:0];
if (myArray.count > 3)
[myArray removeLastObject]; // Crash
But whenever I do this too fast, the last line causes an exception-less crash.
I know that you are not allowed to add or remove objects of an array while it is being enumerated, but I do not enumerate myArray anywhere unless calling count on it performs an implicit enumeration. I also tried doing this:
NSMutableArray *tmp = [myArray mutableCopy];
[tmp removeLastObject];
myArray = tmp; // Crash
But the same thing happens, it crashes on the last line. Again, this works perfectly fine when doing it slowly. The action itself is being called when a user taps a button, and when tapping it too fast, it crashes every time.
EDIT:
I should add that all of this is being run inside the cellForItemAtIndexPath method of a UICollectionView.
First, could you please post the crash message. It would be nice to know what error you are actually seeing.
I wonder what would happen if you switched to immutable arrays. Switch myArray to being an NSArray * and use the following.
myArray = [self updatedArray:myArray withObject:newObject];
Where -updatedArray:withObject: is
- (NSArray *)updatedArray:(NSArray *)array withObject:(id)object {
switch (array.count) {
case 0: return #[object];
case 1: return #[object, array[0]];
default: return #[object, array[0], array[1]];
}
}
Or better for testing
NSArray *temp = [self updatedArray:myArray withObject:newObject];
myArray = temp; // I assume the crash will be here!
If the code crashed at my comment, then deallocating myArray is causing the crash. My guess is that one of the items in the array is pointing to bad memory (a zombie or some such thing).
I can think of two possible reasons for the crash:
1) The array is being accessed/mutated from multiple threads
2) An object inside the array was over-released somewhere, causing it to be deallocated while still living inside the array.
Short answer: try profiling your app in Instruments, and select the "Zombies" instrument. When the crash happens, you just might get a zombie alert, in which case you can get a back-log of everything that led up to your memory getting stomped over.
I think the root cause is this variable is accessed from multiple threads at the same time.
You can use this when access the array
#synchronized(self) {
//Your accessed code here
}
I decided I need to go back over some basics recently, mostly to do with memory management and I'm beginning to doubt.
The reason I ask is because I'm a bit muddled with how to memory safe process items in array using temporary variables.
Could somebody wise in the ways of arc please tell me if this simple code will leak memory?
self.array=[NSMutableArray new];
// Retain +1
Test *obj0 = [[Test alloc] init];
// Retain +1
[self.array addObject:obj0];
Test *obj1 = nil;
//Retain +1
obj1=self.array[0];// does need to be __weak even though it has no owner?
[self.array removeAllObjects];
// is not null
NSLog(#"A: %#", obj1);
When you add obj0 to the array, its retain count will be incremented, see e.g. here. It is then +2.
When you assign it to obj1, it still has an retain count of +2.
When you remove all objects from the array, their retain count will be decremented, so obj0 has again anretain count of +1.
It thus won't leak.
This question already has answers here:
Removing object from NSMutableArray
(5 answers)
Closed 7 years ago.
My program has a NSMutable Array named as "matchedCards", and I have added few object in it of type Card, now I need to remove the objects from the array, and I use the following code for it:
for (Card * removeCards in matchedCards)
{
[self.matchedCards removeObject:removeCards];
}
The first card-object gets removed , and after that the program gets crashed , Can anyone explain the reason behind it, if it successfully removes the first object, why it starts throwing error from 2nd object onwards
You can't remove elements from an array while fast-enumerating it.
If you simply want to remove all objects do
[self.matchedCards removeAllObjects];
If you want to remove only certain elements however, remember their indices in an IndexSet and remove those
NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet new];
for (NSUInteger index = 0; index < [self.matchedCards count]; ++index) {
if (whatever) {
[indexesToRemove addObject:index];
}
}
[self.matchedCards removeObjectsAtIndexes:indexesToRemove];
You can't remove an object from an array while iterating on it. Do this instead:
for (Card * removeCards in [matchedCards copy])
{
[self.matchedCards removeObject:removeCards];
}
Read the crash log. It will say something along the lines of...
"Collection was stated while being enumerated"
Or something like that.
You can't mutate an array while iterating over it using a for:in loop.
You can do this though...
[matchedCards enumerateObjectsUsingBlock:^(Card *removedCards, NSInteger idx, BOOL *stop) {
[self.matchedCards removeObject:card];
}];
Also, with your current code you are actually removing all of the objects from the matchedCards array. It will result in an empty array. Are you sure that's what you want?
The reason is because you are removing the current object and ruin the for-statement
Here's a solution:
for (int i = 0; i < self.matchedCards.count; i++)
{
if ([self.matchedCards[i] isKindOfClass:[YourClass class]])
{
[self.matchedCards removeObject:self.matchedCards[i]];
i--; // invalidate the removed index
}
}
NSLog(#"%#", self.matchedCards);
Take note that i-- is important, else you will not get through to the last element of the array..
Hope this helps you.. Cheers..
// if remove all objects
[matchedCards removeAllObjects];
// if you want to remove using index
for (int i =[matchedCards count]-1; i>=0; i++) {
if (condition) {
[matchedCards removeObjectAtIndex:i];
}
}
I have started out with Objective-C recently and trying to understand the memory management. I came across a peculiar problem yesterday which was causing a memory leak of over 150MB! I traced it down to a piece of code that was creating NSArray literal of NSNumbers. I managed to solve the issue using NSMutableArray (using addObject), but I haven't been able to grasp the concept as to why one method works and the other doesn't. I would love for someone with a better understanding of the memory management to explain the concept behind it so that I can avoid such mistakes in the future.
To better illustrate my question, let me also provide the code snippets. So for instance, I have a function that creates a NSArray of few NSNumbers and returns it and it gets called many times:
-(NSArray *)getNSNumberArray
{
float num1 = 23.56;
float num2 = 75.34;
float num3 = 223.56;
NSArray *numArray = #[[NSNumber numberWithFloat:num1], [NSNumber numberWithFloat:num2], [NSNumber numberWithFloat:num3]];
return numArray;
}
-(void)causeMemoryLeak
{
for (int i = 0; i < 2000000; i++)
{
NSArray *recieverArray = [self getNSNumberArray];
}
}
This results in memory being occupied to be more than 200MB on an iphone6. However if I change my function to be:
-(NSArray *)getNSNumberArray
{
float num1 = 23.56;
float num2 = 75.34;
float num3 = 223.56;
NSMutableArray *numMutableArray = [[NSMutableArray alloc] init];
NSNumber *nsNumber1 = [NSNumber numberWithFloat:num1];
NSNumber *nsNumber2 = [NSNumber numberWithFloat:num2];
NSNumber *nsNumber3 = [NSNumber numberWithFloat:num3];
[numMutableArray addObject:nsNumber1];
[numMutableArray addObject:nsNumber2];
[numMutableArray addObject:nsNumber3];
return numMutableArray;
}
-(void)causeMemoryLeak
{
for (int i = 0; i < 2000000; i++)
{
NSArray *recieverArray = [self getNSNumberArray];
}
}
This does not cause any memory issues.
Maybe I am missing something very obvious, but still cant figure out the reason behind this. Would really appreciate the help on this. There could also be better ways of doing it, and such answers are welcome but basically I am looking for an explanation behind it.
Many thanks in advance!
Attaching links to screenshots showing the memory allocation on device (iphone 6) (I can not attach images here as of now, so have to provide links)
Approach 1 Memory Allocation (memory is retained and not freed up): https://drive.google.com/open?id=0B-a9WJSBuIL4bTR5RTVuaWpqYkE&authuser=0
Approach 2 Memory Allocation (memory is freed up and there is no surge in memory allocation as well): https://drive.google.com/open?id=0B-a9WJSBuIL4QzQzbGYyQzZDdW8&authuser=0
I tried your code because I really didn't found any reason for memory leak and there is no memory leak.
When you use the regular array, the device runs all the code, and releases the memory only at the end. This causing the heap to alloc 250 MB, and release them at the end.
If you use the mutable array, the device releases the previous array before creating the new one, so there is no point with to many allocation.
I guess that the different may be caused by run time complexity of the called function in the loop.
As you are saying, only one approach is causing surge in memory, it does not seem so. According to the documentation, NSMutableArray uses slightly more memory because it can change size, it can't store the contents inside the object and must store a pointer to out of line storage as well as the extra malloc node for the storage.
In both the approaches, memory is freed up instantly, there is no issue with me.
Update 1 :
I have noticed that if you use the array, for example if you log the array, the memory is freed up instantly in your first approach. It is happening as you do not use that array. It solves your problem.Try this code :
-(NSArray *)getNSNumberArray
{
float num1 = 23.56;
float num2 = 75.34;
float num3 = 223.56;
NSArray *numArray = #[[NSNumber numberWithFloat:num1], [NSNumber numberWithFloat:num2], [NSNumber numberWithFloat:num3]];
return numArray;
}
-(void)causeMemoryLeak
{
for (int i = 0; i < 2000000; i++)
{
NSArray *recieverArray = [self getNSNumberArray];
NSLog(#"%#", recieverArray);
}
}
First, you need to read up on "autorelease" and "autorelease pools". Objects often are kept inside an autorelease pool and only disappear when the autorelease pool disappears. So a loop iterating two million times without using an autorelease pool is a bad, bad idea. You don't actually have a memory leak, the objects will all be released some time after the method returns.
Most methods returning objects return autoreleased objects, but the result of [[xxx alloc] init] is not autoreleased, so there you have the difference. But also ARC is quite clever, and if a method returns an autoreleased object and the caller immediately retains the object, the object will not actually be added to the autorelease pool. That's what happened when the object was passed to NSLog; in order to do that, it had to be retained first. No autorelease pool. In your code, the object was never used, so that optimisation didn't happened.
Of course your code is really useless, so you were punished for writing useless code.
I'm trying to get my mind around one aspect of memory management in the iPhone SDK.
If I ran:
for (int x = 0; x < 10; x++) {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
}
Am I creating 10 myArray objects in memory, or does each alloc overwrite the previous? If the latter, I presume I would only need one [myArray release] after the loop to clean up. If the former, I presume I need the release inside the For loop.
Thanks.
You get ten different allocations and you need to release them if you do not want memory leaks.
for (int x = 0; x < 10; x++) {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
....
[myArray release];
}
If you don't release, the leaked object would actually 10, not 9 as per comment, since outside of the loop you don't have access to the loop local variable and the last allocated object would also be unreachable.
Actually, you have 10 objects with 10 leaks. Once you leave the loop, myArray is no longer in scope (and is therefore inaccessible), so there is no way to release the 10th allocation either.
You're creating 10 objects, 9 of which are leaked.
You should release after you use them in the end of the loop.
This also not only about iPhone SDK. It's basic Cocoa memory management. Also works on the Mac.
In that case you are creating 10 different Array objects each one with a retain count of 1, and no reference whatsoever. This would be a "memory leak factory" with the 10 objects never beign released from the memory. :)
oooops... did not see the release...9 leaked arrays.
In addition to what everyone (rightly) said, Cocoa also supports autoreleased objects. If you rephrase your snippet thus:
for (int x = 0; x < 10; x++)
{
NSMutableArray *myArray = [NSMutableArray arrayWithObjects: ...];
//....
}
you still allocate 10 different arrays, but none are leaked. They are autoreleased eventually.
for (int x = 0; x < 10; x++) {
NSMutableArray *myArray = [NSMutableArray array]; //Its an autorelease
....
}
This creates 10 different NSMutableArray objects. You actually do not need to release them explictly.myArray is autoreleased at the end of the run loop.
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
In NSMutableArray *myArray = [NSMutableArray array];, you do not take ownership of the array, and it will be passed to you autoreleased.
You can learn more about memory management here.