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(...)
的参数在限制范围内!
干杯)
最奇怪的事情。我有两种以完全相同的方式使用可选枚举的方法。一种功能始终有效,无论是在设备中还是在模拟中。然而,第二种方法只适用于模拟器,有时在设备上。我很确定 "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(...)
的参数在限制范围内!
干杯)