Search code examples
swiftloopsfor-loopfor-in-loop

C-Style for loop works fine but changing to Swift for-in causes out of bounds errors


So I have this simple for loop that causing me major aggravation. See, it works just fine using a C-Style for-loop (commented). But swift throws a warning (not an error) that C-Style loops are going to be depreciated in the future, so I figured I should change it.

When I try changing it, however, I get an out of bounds error which never happened with the C-Style loop. The error occurs at the if statement (so only if the mouse is over the cheese). And I don't see why it should work with a C loop and not a for-in loop.

func checkCheese(){
    //for(var i = 0; i < cheese.count; i += 1){
    for i in 0 ..< cheese.count {
        print(i) //prints just fine every time
        if CGRectIntersectsRect(mouse.node.frame, cheese[i].node.frame) {//throws the out of bounds error
            cheese[i].node.removeFromParent() 
            cheese.removeAtIndex(i) //the culprit?
        }
    }
}

Any help would be greatly appreciated.

I'd also like an explanation why the loops behave differently if possible. Thanks


Solution

  • cheese.count is only being evaluated once. Suppose you start with 3 items in cheese. The loop will iterate over indices 0, 1, 2. If you remove item 1, then the old 2 becomes the new 1, and there is no longer an item at index 2. However, the loop will continue to index 2, just as it was set out to do from the beginning.

    To fix this, every time you remove, deincrement your index:

    for i in 0 ..< cheeses.count {
        print(i) //prints just fine every time
        if CGRectIntersectsRect(mouse.node.frame, cheeses[i].node.frame) {//throws the out of bounds error
            cheeses[i].node.removeFromParent() 
            cheeses.removeAtIndex(i) //the culprit? ... no longer!
            i -= 1 //fixed!
        }
    }
    

    Now, that solves the error for the solution as you present it, but I propose a cleaner solution:

    cheeses.filter{ cheese in
              if CGRectIntersectsRect(mouse.node.frame, cheese.node.frame {
                  cheese.node.removeFromParent()
                  return false //don't keep this cheese
              }
              else {
                  return true //keep this cheese          
              }
          }