0

In this loop we have the potential to reduce the number of items in the loop while processing it. This code works fine in Obj-C, but the Swift loops don't get the message that an item has been removed and end up overflowing the array.

In Objective-C, we had:

            for(int i = 4; i < staticBlocks.count; i++)
            {
                PlayerSprite* spr = [staticBlocks objectAtIndex:i];
                [spr setPosition:CGPointMake(spr.position.x, spr.position.y-1)];

                if(spr.position.y < -1000)
                {
                    [staticBlocks removeObject:spr];
                    [spr removeFromParent];
                }

                if(spr.blockTypeIndex == Block_Type_Power_Up)
                {
                    [spr update];
                }
            }

In Swift I know of these options:

               //for i in 4.stride(to: staticBlocks.count, by: 1){ //crashes
               //for i in 4..<staticBlocks.count{  //crashes

               for var i = 4; i < staticBlocks.count; i += 1 { //works, but is deprecated

                    let spr = staticBlocks.objectAtIndex(i) as! PlayerSprite
                    spr.position = CGPointMake(spr.position.x, spr.position.y-1)

                    if(spr.position.y < -1000)
                    {
                        staticBlocks.removeObject(spr)
                        spr.removeFromParent()
                        //break
                    }

                    if(spr.blockTypeIndex == k.BlockType.PowerUp)
                    {
                        spr.update()
                    }
                }

In this specific case, it really isn't a problem for me to use a break statement (which is currently commented out) to kill the loop and prevent the crash, but it doesn't seem like the proper fix. I assume there will come a time when I need to know how do do this correctly. Is there a non deprecated way to do a for loop, one which processes the count each pass?

A related, unanswered question. Is the for loop condition evalutaed each loop in swift?

Community
  • 1
  • 1
Millawub
  • 1
  • 1

2 Answers2

0

This is because in Swift you cannot remove items from an array while you are iterating over it.

From this question Removing from array during enumeration in Swift? you can see that you should be using the filter function instead of using a for loop.

For example, don't do this:

for (index, aString: String) in enumerate(array) {
    //Some of the strings...
    array.removeAtIndex(index)
}

Do something like this:

var theStrings = ["foo", "bar", "zxy"]

// Filter only strings that begins with "b"
theStrings = theStrings.filter { $0.hasPrefix("b") } 

(Code example from this answer)

Additionally, it should be noted that filter won't update the array, it will return a new one. You can set your array to be equal to that array afterwards.

An additional way to solve the issue, from the same question is to do this:

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
    a.removeAtIndex(i)
}
print(a)
Community
  • 1
  • 1
Wyetro
  • 8,439
  • 9
  • 46
  • 64
  • I'm struggling to understand how to use filter in my specific situation. I have not used it before, perhaps it can do what I want but I don't really understand. What I did realize is that the answer directly above the one you linked works for my situation. – Millawub Aug 11 '16 at 06:37
  • I edited my answer to include that part of the code too. – Wyetro Aug 11 '16 at 11:24
0

I don't know how to link to a specific answer, but this code did what I needed. Marking as duplicate now.

Removing from array during enumeration in Swift?

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
    a.removeAtIndex(i)
}
Community
  • 1
  • 1
Millawub
  • 1
  • 1