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
}
Related
I have a some permutation/combination code that is iterating through 20 objects taking 5 at a time. When the list meets some criteria I print the objects that make up that list. Needless to say the loop is rather large. I place all of the combinations in an NSMutableArray inside of the loop. Once the objects have been added and pass/fail the test, I remove all of the object from the array. (Psuedo code below).
-(void)CreateCombinations
{
NSMutableArray *Combinations = [[NSMutableArray alloc] init];
#autoreleasepool {
NSArray *objectsList = [[NSArray alloc] initWithObjects:
#“Lisa”,#“Kevin”,…nil];
} //autorelease pool
while(!Finished)
{
Combinations = [self getNextCombo: Combinations]
if (goodCombination)
[self printCombos:Combinations]
[Combinations removeAllObject];
}
}
While monitoring the debug session, CPU and memory are at capacity. I am sure the looping is coming into play. I don't believe that I am reallocating the 'Combinations' Array for every iteration. If I am, is there something that I can do to make sure that it is properly deallocated or released before the next iteration of the loop?
When I add the #autorelease (which is before the loop) I get a "use of undeclared identifier error".
The code you've shown is completely fake so it's impossible to guess what your real code is doing wrong. But if you are using memory because you are piling up autoreleased objects during a loop, wrap the interior of the loop in an #autoreleasepool block:
while (...) {
#autoreleasepool {
// do stuff
}
}
The idea is to release the temporarily used memory every time through the loop.
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 watching an NSArray property with KVO. I've implemented KVC like in this post and I also implemented most of the KVC array accessors. To mutate it, I use mutableArrayValueForKey. It works fine, except to 2 issues:
When I call removeAllObjects, I get a NSKeyValueChangeRemoval change for each single removed item. I'd like to receive only one NSKeyValueChangeRemoval notification with all removed indexes in it.
Similarly when I call addObjectsFromArray:, I get NSKeyValueChangeInsertion for each single added item. I'd like to receive only one NSKeyValueChangeInsertion notification with all added indexes in it.
Notice that I do have implemented the KVC methods remove<Key>ItemsAtIndexes: and insert<Key>Items:atIndexes:. They are not called though.
I use the following workarounds:
- (void)removeAllObjectsWorkaroundFromArray:(NSMutableArray *)modelArray {
NSRange indexRange;
indexRange.length = modelArray.count;
indexRange.location = 0;
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:indexRange];
[modelArray removeObjectsAtIndexes:indexSet];
}
- (void)addObjectsFromArrayWorkaroundWithArray:(NSMutableArray *)modelArray arrayToAdd:(NSArray *)arrayToAdd {
NSRange indexRange;
indexRange.length = arrayToAdd.count;
indexRange.location = modelArray.count;
NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:indexRange];
[modelArray insertObjects:arrayToAdd atIndexes:indexSet];
}
Is there a way to directly use removeAllObjects and addObjectsFromArray: without the need for the above workarounds?
As I am sure you are aware one cannot observe the array itself, just attributes of it. So I think your workaround is unavoidable.
That being said - I really like the way you solved this!
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
index 0 beyond bounds for empty array error
I am a hobbyist in iOS programming. And I always have the same problem while programming.
I think that I don't understand the memory management issue.
This is my actual project:
h-File:
a NSMutableArray containing NSStrings
NSMutableArray *pictureStrings;
#property (nonatomic,retain) NSMutableArray *pictureStrings;
in the m-File:
#synthesize pictureStrings;
and in the viewDidLoad Method
pictureStrings = [[NSMutableArray alloc] init];
in the dealloc Method
[pictureStrings release];
okay. I am working with an asynchronous URL Request which gets a number of strings and in the connectionDidFinishLoading Delegate Method the values get stored to pictureStrings
[pictureStrings addObject:aString];
BUT
when I read values in the cellForRowAtIndexPath Method like
if (pictureStrings != nil) {
cell.textLabel.text = [NSString stringWithFormat:#"%#", [pictureStrings objectAtIndex:indexPath.row]];
}
my App crashes with the message:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array'
Where is my fault?
Please help me!
Yours, Raphael.
Greetings from Austria
Your crash is not a memory management issue. Your crash is that you're reading past the end of an array. We know that pictureStrings is non-nil at that point, because if it was nil then the objectAtIndex: method would have silently just returned nil (because messaging nil returns nil [1]). Oh and not to mention you have the if around the call to objectAtIndex: anyway - but you can safely remove that really as you shouldn't need it.
So, the only thing that can really be happening here is that your [pictureStrings addObject:aString] are not being called before the table view is reloaded. However, that would be quite odd because I assume you are doing something like return pictureStrings.count in the table view's tableView:numberOfRowsInSection: data source method. If there really were zero elements in the array then the table view wouldn't be asking for any rows and you wouldn't get this crash.
I think your problem is likely to be that you're not adding the strings to the array. You could check by breakpointing that code and seeing what is happening.
Also, consider changing your code to set the cell's text to this:
cell.textLabel.text = [pictureStrings objectAtIndex:indexPath.row];
You said they are already strings so why bother going through a stringWithFormat: call?
[1] Caveat: Doesn't always return nil - read up about it for more information.
Car class
--------------
price
color
crash code is:
NSMutableArray *list = [[NSMutableArray alloc] init];
Car *car = [[Car alloc] init];
car.price = 10;
car.color = 1;
[list addObject:car];
// some code
[list removeAllObjects]; // Crash here
why crash, how can i resolve it.
app exit with nothing output
I dont know what you have in the "someCode" section in your segment. You first comment out that code and check if the app crashes. If still it crashes then only consider what I have given below. I mean you make sure there is nothing wrong with your code before going for workarounds :)
just try this code, and see if it crashes now.I know it doesn't make sense, but it happened to me once too. Once when array count was zero removeAllObjects crashed for me. I doubt an SDK bug somewhere there :(
if([list count]){
[list removeAllObjects];
}
Most likely you are releasing one or more of the objects in the array one too many times. When the NSMutableArray tries to release that object, it crashes because the object has already been disposed of.
I just ran into this same thing. In the dealloc method of my object I had:
-(void) dealloc
{
[super dealloc];// <--- le' culprit!
[Image_ID release];
[Image_Number release];
[Image_UID release];
[Series_ID release];
[Series_UID release];
[Study_UID release];
// <--- it should be here...
}
I moved [super dealloc] below all the releases..and my [.... removeAllObjects] worked fine..
At a guess, I'd say you're releasing list somewhere, and so your call to removeAllObjects is being sent to a deallocated instance. Impossible to be sure without a stack trace and some more detail, though—is it an EXC_BAD_ACCESS error or what?
Set the array property as Retain in interface section. Once I did same it worked for me. just try once.