i have a strange issue , if i remove my item at forin enumeration , it would crash , so like this:
for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
//this if else not so important for happening crash
if(obstacleToTrack.distance > 0){
obstacleToTrack.distance -= _playerSpeed * _elapsed;
}else{
if (obstacleToTrack.watchOut) {
obstacleToTrack.watchOut = NO;
}
obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
}
if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
[self removeChild:obstacleToTrack];
//this line makes crash happen , if remove this line code work fine
[_obstaclesToAnimate removeObject:obstacleToTrack];
}
}
if i change my code to
NSMutableArray *forRemoving = [[NSMutableArray alloc]init];
for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
//this if else not so important for happening crash
if(obstacleToTrack.distance > 0){
obstacleToTrack.distance -= _playerSpeed * _elapsed;
}else{
if (obstacleToTrack.watchOut) {
obstacleToTrack.watchOut = NO;
}
obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
}
if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
// code change here
[self removeChild:obstacleToTrack];
[forRemoving addObject:obstacleToTrack];
}
}
for(Obstacle *obstacleToTrack in forRemoving){
[_obstaclesToAnimate removeObject:obstacleToTrack];
[forRemoving removeObject:obstacleToTrack];
}
[forRemoving release];
this would work perfect , could someone tell me why?
The answer is that if you remove an object the other objects in that array move postion in the array since an item is removed.
For example we have an array with 4 items, if we remove the first item (item 0) the item that used to be at index 1 is now at index 0 and the item at 2 is now at 1.
Thus the enumeration breaks.
You could solve this by looping thru the array from the count down to 0:
for (int i = [array count]-1; i >= 0; i--) {
id object = [array objectAtIndex:i];
if (some check) {
[array removeObjectAtIndex:i];
}
}
Like rckoenes said, you break the enumaration by removing stuff in the array while iterating through it.
What you can do is to have a second array where you insert the objects that you want to remove. Then, after your enumeration is finished you can remove all the objects that are found in the second array, from your first array.
You must not modify a collection while iterating through its items.
If you iterate index based (i.e. the classical for loop), you can remove things, but be careful about adjusting your index.
Check this link
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/Collections/Articles/Enumerators.html
Enumeration is “safe”—the enumerator has a mutation guard so that if
you attempt to modify the collection during enumeration, an exception
is raised.
Related
I have an array in an old objective-C app that I am using to learn more "complicated" coding. It is back from the old days of OS X and was very much broken. I have gotten it to work (mostly)! However, the app has an NSMutableArray of images, 7 in total. I use a random number generator to insert the images on the screen, some code to allow them to fall, and then, using screen bounds, when they reach "0" on the Y axis they are removed from the array.
I initially just had:
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
[flakesArray removeObject:myItem];
I have read when removing objects from an array it is best practice to iterate in reverse...so I have this bit of code:
for (NSInteger i = myArray.count - 1; i >= 0; i--)
{ //added for for statement
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
[myArray removeObjectAtIndex:i];
}
Sadly both methods result in the same mutated while enumerated error. Am I missing something obvious?
If I add an NSLog statement I can get, I think, the index of the item that needs to be removed:
NSLog (#"Shazam! %ld", (long)i);
2017-01-07 14:39:42.086667 MyApp[45995:7500033] Shazam! 2
I have looked through a lot and tried several different methods including this one, which looks to be the most popular with the same error.
Thank you in advance! I will happily provide any additional information!
Adding more:
Sorry guys I am not explicitly calling NSFastEnumeration but I have this:
- (void) drawRectCocoa:(NSRect)rect
{
NSEnumerator* flakesEnum = [flakesArray objectEnumerator];
then
for( i = 0; i < numberToCreate; i++ )
{
[self newObject:self];
}
while( oneFlake = [flakesEnum nextObject] )
It is here where:
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
NSLog (#"Shazam! %i", oneFlake);
[flakesArray removeObject:oneFlake];
}
Thank you all. I am learning a lot from this discussion!
There are two ways to go: (1) collect the objects to remove then remove them with removeObjectsInArray:.
NSMutableArray *removeThese = [NSMutableArray array];
for (id item in myArray) {
if (/* item satisfies some condition for removal */) {
[removeThese addObject:item];
}
}
// the following (and any other method that mutates the array) must be done
// *outside of* the loop that enumerates the array
[myArray removeObjectsInArray:removeThese];
Alternatively, reverseObjectEnumeration is tolerant of removes during iteration...
for (id item in [myArray reverseObjectEnumerator]) {
if (/* item satisfies some condition for removal */) {
[myArray removeObject: item];
}
}
As per the error, you may not mutate any NSMutableArray (or any NSMutable... collection) while it is being enumerated as part of any fast enumeration loop (for (... in ...) { ... }).
#danh's answer works as well, but involves allocating a new array of elements. There are two simpler and more efficient ways to filter an array:
[array filterUsingPredicate:[NSPredicate predicateWithBlock:^(id element, NSDictionary<NSString *,id> *bindings) {
// if element should stay, return YES; if it should be removed, return NO
}];
or
NSMutableIndexSet *indicesToRemove = [NSMutableIndexSet new];
for (NSUInteger i = 0; i < array.count; i += 1) {
if (/* array[i] should be removed */) {
[indicesToRemove addIndex:i];
}
}
[array removeObjectsAtIndexes:indicesToRemove];
filterUsingPredicate: will likely be slightly faster (since it uses fast enumeration itself), but depending on the specific application, removeObjectsAtIndexes: may be more flexible.
No matter what, if you're using your array inside a fast enumeration loop, you will have to perform the modification outside of the loop. You can use filterUsingPredicate: to replace the loop altogether, or you can keep the loop and keep track of the indices of the elements you want to remove for later.
I am using a nsmutablearray in loop and want to remove its object (or assign nil) that has just traversed.
But if I am doing so, I get an error as <__NSArrayM: 0x8c3d3a0> was mutated while being enumerated.' . The code is as below
- (TreeNode*)depthLimitedSearch:(TreeNode *)current costLimit:(int)currentCostBound {
NSMutableArray *children=[NSMutableArray arrayWithArray:[current expandNodeToChildren]];
for (TreeNode *s in children) {
if (s.puzzleBox.isFinalPuzzleBox) {//checking for final puzzleBox
return s;
}
/*exploredNodes++;
if (exploredNodes %10000==0) {
NSLog(#"explored nodes for this treshold-%d are %d",currentCostBound,exploredNodes);
}*/
int currentCost =[s.cost intValue]+[s.heuristicsCost intValue];
if (currentCost <= currentCostBound) {
//[s.puzzleBox displayPuzzleBox];
TreeNode *solution = [self depthLimitedSearch:s costLimit:currentCostBound];
if (solution!=nil){//&& (bestSolution ==nil|| [solution.cost intValue] < [bestSolution.cost intValue])) {
bestSolution = solution;
return bestSolution;
}
}else {
if (currentCost < newLimit) {
//NSLog(#"new limit %d", currentCost);
newLimit = currentCost;
}
}
// here I want to free memory used by current child in children
[children removeObject:s]
}
children=nil;
return nil;
}
and I have commented the place where I want to release the space used by the child.
You should not be using a for...in loop if you want to remove elements in the array. Instead, you should use a normal for loop and go backwards in order to make sure you don't skip any items.
for (NSInteger i = items.count - 1; i >= 0; i--) {
if (someCondition) {
[items removeObjectAtIndex:i];
}
}
You can collect the items to be removed in another array and remove them in a single pass afterwards:
NSMutableArray *toRemove = [NSMutableArray array];
for (id candidate in items) {
if (something) {
[toRemove addObject:candidate];
}
}
[items removeObjectsInArray:toRemove];
It’s easier than iterating over indexes by hand, which just asking for off-by-one errors.
Not sure how this plays with your early returns, though.
i'm trying to get the values of an array randomly but i'm getting an error
here is my code so far:
NSMutableArray *validMoves = [[NSMutableArray alloc] init];
for (int i = 0; i < 100; i++){
[validMoves removeAllObjects];
for (TileClass *t in tiles ) {
if ([self blankTile:t] != 0) {
[validMoves addObject:t];
}
}
NSInteger pick = arc4random() % validMoves.count;
[self movePiece:(TileClass *)[validMoves objectAtIndex:pick] withAnimation:NO];
}
The error you're getting (an arithmetic exception) is because validMoves is empty and this leads to a division by zero when you perform the modulus operation.
You have to explicitly check for the case of an empty validMoves array.
Also you should use arc4random_uniform for avoiding modulo bias.
if (validMoves.count > 0) {
NSInteger pick = arc4random_uniform(validMoves.count);
[self movePiece:(TileClass *)[validMoves objectAtIndex:pick] withAnimation:NO];
} else {
// no valid moves, do something reasonable here...
}
As a final remark not that arc4random_uniform(0) returns 0, therefore such case should be avoided or you'll be trying to access the first element of an empty array, which of course will crash your application.
I have an NSMUtable array of images where a different image is displayed with a previous and a next button, but when I get to the end of the array the simulator crashes. I want to loop the end of the array to the beginning so that when I get to the end of the array of images when I hit the next button again it loops back to the first image, also when Im at the first image if I hit the previous button it loops to the last image with no crashes
What you want is a circular array, which is easy to implement using a standard NSMutableArray. For instance, say you store your images in an array called imageArray and use a simple variable to keep track of the index of your current image, like:
int currentImageIndex = 0;
...then you might implement nextImage and previousImage like:
- (UIImage*) nextImage {
currentImageIndex = (currentImageIndex + 1) % [imageArray count];
return [imageArray objectAtIndex:currentImageIndex];
}
- (UIImage*) previousImage {
currentImageIndex--;
if (currentImageIndex < 0) {
currentImageIndex = [imageArray count] - 1;
}
return [imageArray objectAtIndex:currentImageIndex];
}
Then just use nextImage and previousImage whenever you want to step through the array, and problem solved.
Simple enough to do. all you need to do is create a check to see if you're on the last element, and if so, set your tracker (like count or i etc) to 0 again,
Heres the psudo code
//set index
if ( array [ index ] == len(array) - 1) //at end
{
index = 0
}
if(array [index] == -1)//at beginning
{
index = len(array) -1
}
// do something with array[index]
I want to propose this solution:
A property for the selected UIImage, a NSInteger property to hold the current index.
-(IBAction) nextButtonTapped:(id)sender
{
self.currentIndex = self.currentIndex++ % [self.images count];
self.selectedImage= [self.images objectAtIndex:self.currentIndex];
[self reloadImageView];
}
-(IBAction) previousButtonTapped:(id)sender
{
self.currentIndex--;
if (self.currentIndex < 0)
self.currentIndex += [self.images count];
self.selectedImage= [self.images objectAtIndex:self.currentIndex];
[self reloadImageView];
}
-(void)reloadImageView
{
//do, what is necessary to display new image. Animation?
}
People I'd like to see if an element at index[i] is not present in a different (or current ) array.
So for instance if my array looked somewhat like this
[wer, wer4 , wer5 , werp , klo ...
Then I would like to see if aExerciseRef.IDE ( which is "sdf" for instance ) does or does not exist at index[i].
I assume I'd have to use some sort of iterator ..
for( int i = 0; i < 20; i++ )
{
if([instruct objectAtIndex:index2 + i] != [instruct containsObject:aExerciseRef.IDE] )
NSLog(#"object doesn't exist at this index %i, i );
else
NSLog(#"well does exist")
}
I know this doesn't work , it's just to elaborate what I'd like to achieve.
edit:
I'm going to try to elaborate it a little more and be more specific.
1) First of all aExerciseRef.IDE changes everytime it get's called so one time it is "ret" another time it's "werd".
2) Imagine an array being filled with aExerciseRef.IDE's then I would like to compare if the elements in this array exist in the instruct array.
So I would like to see if the element at let's say position 2 (wtrk)
[wer, wtrk, wer , sla ...
exists in the array which was being filled with the aExerciseRef.IDE's.
I hope I've been more clear this time.
Sir Lord Mighty is partially correct. Yes, your comparison is wrong. No, his solution won't work.
Here's one that will:
if (index < [anArray count] && [[anArray objectAtIndex:index] isEqual:anObject]) {
NSLog(#"Got it!");
} else {
NSLog(#"Don't have it.");
}
Alternatively, you can use the containsObject: method to achieve the same thing:
if ([anArray containsObject:aExerciseRef.IDE]) {
NSLog(#"Got it!");
} else {
NSLog(#"Don't have it.");
}
The second option will not give you the index of the object, but that is easily rectified using:
NSInteger index = NSNotFound;
if ([anArray containsObject:aExerciseRef.IDE]) {
index = [anArray indexOfObject:aExerciseRef.IDE];
...
}
Your example makes no sense at all, and does nothing to clarify the question. You are doing a comparison between an expression with type (id)
[instruct objectAtIndex:index2 + i]
and one with type BOOL
[instruct containsObject:aExerciseRef.IDE]
And if the object you are looking for is at index x in an array, then it goes without saying that containsObject on the array will return YES for that object.
If what you want to accomplish is simply what you state in the title, then it's as easy as:
if ([[anArray objectAtIndex:index] == anObject])
NSLog (#"Got it!");
else
NSLog (#"Don't have it.");