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
        }
    }
}
circuitlego
  • 3,469
  • 5
  • 22
  • 22

2 Answers2

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.

circuitlego
  • 3,469
  • 5
  • 22
  • 22
  • Sounds like a different question. – Mike D Jan 16 '15 at 00:34
  • 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? – circuitlego Jan 16 '15 at 00:48
  • 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. – Simon Jan 17 '15 at 11:58
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)

Community
  • 1
  • 1
irq033
  • 71
  • 4