0

The strangest thing. I have two methods that use an optional enum in the exact same way. One function always works, both in device and simulated. However, the second method only works on the simulator and sometimes on the device. I'm pretty sure that "when" it works is not related to the value from testing that I have done. For example, I always call the method first with a nil value, and only sometimes does it crash.

The error is an EXC_ARM_BREAKPOINT, prints no messages, and the stacktrace is of no help. The debugger shows the optional's value as "Some" even after checking for nil, which is weird because I can't print it (it crashes when trying).

The failing code:

class LevelElevenState: GameState {
     init(count: Int, var goalNumber: GameNumber?) { 
         super.init(stateType: .Normal)
         self.transition = {
            (actionDone: GameAction) -> GameState? in

            switch actionDone {
            case .Pressed(let number):

                if goalNumber == nil {  //FAILS HERE
                    goalNumber = number //OR HERE
                }

                if goalNumber == number {
                    self.delegate?.switchIndicators(true, lights: [GameNumber(rawValue: count)!])


                    if count < 5 {
                        let direction = GameDirection(rawValue: Int(arc4random()) % 2)!
                        let amount = Int(arc4random_uniform(98)) + 1
                        var nextRaw = number.rawValue
                        switch direction {
                        case .Up:
                            nextRaw = (nextRaw + amount) % 5
                        case .Down:
                            nextRaw = ((nextRaw - amount) % 5) + 5
                        }
                        let nextNumber = GameNumber(rawValue: nextRaw)

                        let phrase = "\(direction), \(amount)"
                        self.delegate?.speakPhrase(phrase)

                        return LevelElevenState(count: count + 1, goalNumber: nextNumber)
                    } else {
                        return GameState.goal()
                    }
                }else {
                    return GameState.mistake()
                }
            default:
                return nil
            }
        }
    }
}

class LevelEleven: GameStateLevel, GameStateLevelDelegate {

    override init() {
        super.init()
        levelDelegate = self
        index = 10
    }

    func initialState() -> GameState {
        return LevelElevenState(count: 1, goalNumber: nil)
    }
}

The following code NEVER crashes. I personally don't see the difference.

class LevelSevenState: GameState {
    var count = 0

    init(goal: Int, var goalNumber: GameNumber?) {

        super.init(stateType: .Normal)
        self.transition = {
            (actionDone: GameAction) -> GameState? in

            switch actionDone {
            case .Pressed(let number):
                if goalNumber == nil {
                    goalNumber = number
                }

                if goalNumber == number {
                    self.count++

                    if self.count == goal {
                        if let indicator = GameNumber(rawValue: goal) {
                            self.delegate?.switchIndicators(true, lights: [indicator])
                        }

                        if goal < 5 {
                            return LevelSevenState(goal: goal + 1, goalNumber: goalNumber)
                        } else {
                            return GameState.goal()
                        }
                    }else {
                        return nil
                    }
                }else {
                    return GameState.mistake()
                }
            default:
                return nil
            }
        }
    }
}

class LevelSeven: GameStateLevel, GameStateLevelDelegate {

    override init() {
        super.init()
        levelDelegate = self
        index = 6
    }

    func initialState() -> GameState {
        return LevelSevenState(goal: 1, goalNumber: nil)
    }
}

Edit: As requested

enum GameNumber: Int {
    case One = 1
    case Two = 2
    case Three = 3
    case Four = 4
    case Five = 5

    init?(myRaw: Int) {
        if let fromRaw = GameNumber(rawValue: myRaw) {
            self = fromRaw
        }else {
            return nil
        }
    }

    init(color: GameColor) {
        switch color {
        case .Blue:
            self = .One
        case .Green:
            self = .Two
        case .Yellow:
            self = .Three
        case .Orange:
            self = .Four
        case .Red:
            self = .Five
        }
    }
}
4
  • Does it run when it isn't connected to the debugger? Just wondering if you have a breakpoint (possibly symbolic) that is triggering. Can you resume the code after it stops or is it a crash? Commented Jan 15, 2015 at 19:57
  • It's a crash, and no it doesn't work without the debugger either. Commented Jan 15, 2015 at 19:58
  • Can you add the definition of GameNumber and any classes/structs it references? Commented Jan 15, 2015 at 20:51
  • Added the definition. Commented Jan 15, 2015 at 22:02

2 Answers 2

1

Yes, that's the piece Int(arc4random()) that's crashing. The reason is that arc4random() returns UInt32, which might (randomly!) exceed Int.max and cause Int(...) to crash -- as answered here. There is a number of ways to avoid this, for instance, you can first calc the remainder, and then pass it to Int(...) :

Int(arc4random() % 2)

Or you can use

Int(arc4random_uniform(2))

Just make sure the argument of Int(...) is within limits! Cheers)

Sign up to request clarification or add additional context in comments.

Comments

1

I found the problem. The reason it was so difficult to debug was that the error has nothing to do with what I thought. XCode was breaking on the wrong line. This was the line that was making the error:

Int(arc4random()) % 2

I don't know exactly why that makes swift crash, but that's definitely it. If anyone has any insight to the problem, I will gladly accept an answer that addresses that.

3 Comments

Sounds like a different question.
I'm not super familiar with SO's best practices, so I would appreciate some feedback on how to deal with this. Should I delete this question as it's misleading, and make a new one that people would be more likely to find through search?
U should mark ur own answer, or the answer above which describes this a little better as well als accepted answer. If you have another question, like why does swift crash on this, you should open another topic, maybe with a link to this one in the description.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.