Swift 当检查 nil 时,Optional 有时会在设备上崩溃,但不会在模拟器上崩溃

Swift Optional sometimes crashes on device but not on simulator when checking nil

最奇怪的事情。我有两种以完全相同的方式使用可选枚举的方法。一种功能始终有效,无论是在设备中还是在模拟中。然而,第二种方法只适用于模拟器,有时在设备上。我很确定 "when" 它的工作原理与我所做的测试的值无关。例如,我总是先用一个 nil 值调用该方法,只是有时会崩溃。

错误是 EXC_ARM_BREAKPOINT,未打印任何消息,堆栈跟踪也无济于事。即使在检查了 nil 之后,调试器仍将可选值显示为 "Some",这很奇怪,因为我无法打印它(尝试时崩溃)。

失败代码:

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)
    }
}

以下代码永远不会崩溃。我个人看不出有什么不同。

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)
    }
}

编辑:按要求

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
        }
    }
}

我发现了问题。之所以这么难调试,是因为这个错误跟我想的一点关系都没有。 XCode 在错误的行上中断。这是出错的行:

Int(arc4random()) % 2

我不知道为什么会导致 swift 崩溃,但肯定是这样。如果有人对这个问题有任何见解,我很乐意接受解决这个问题的答案。

是的,这就是崩溃的部分 Int(arc4random())。原因是 arc4random() returns UInt32,它可能(随机地!)超过 Int.max 并导致 Int(...) 崩溃——如回答 here . 有很多方法可以避免这种情况,例如,您可以先计算余数,然后将其传递给 Int(...)

Int(arc4random() % 2)

或者您可以使用

Int(arc4random_uniform(2))

只要确保Int(...)的参数在限制范围内! 干杯)