将arc4random()的结果转换为Int时崩溃
我写了一个简单的Bag类。 一个袋子里装满了固定比例的温度枚举。 它允许您随机抓取一个,并在空的时候自动重新填充。 它看起来像这样:
class Bag { var items = Temperature[]() init () { refill() } func grab()-> Temperature { if items.isEmpty { refill() } var i = Int(arc4random()) % items.count return items.removeAtIndex(i) } func refill() { items.append(.Normal) items.append(.Hot) items.append(.Hot) items.append(.Cold) items.append(.Cold) } }
温度枚举看起来像这样:
enum Temperature: Int { case Normal, Hot, Cold }
我的GameScene:SKScene
有一个不断的实例属性bag:Bag
。 (我也尝试了一个variables。)当我需要一个新的温度时,我调用了bag.grab()
,一次在didMoveToView
,在适当的时候touchesEnded
。
随机地,这个调用在Bag.grab()
的if items.isEmpty
行中Bag.grab()
。 错误是EXC_BAD_INSTRUCTION
。 检查debugging器显示的项目size=1
和[0] = (AppName.Temperature) <invalid> (0x10)
。
编辑看起来像我不明白的debugging器信息。 即使有效的数组显示size=1
和[0] =
无关的值。 所以没有帮助。
我无法让它在一个游乐场孤立崩溃。 这可能是明显的,但我很难过。
函数arc4random
返回一个UInt32
。 如果您得到的值高于Int.max
,则Int(...)
Int.max
将会崩溃。
运用
Int(arc4random_uniform(UInt32(items.count)))
应该是更好的解决scheme。
(怪怪的Alpha版本的崩溃消息…)
我发现解决这个问题的最好方法是使用rand()而不是arc4random()
在你的情况下,代码可能是:
var i = Int(rand()) % items.count
该方法将在给定的最小值和最大值之间生成一个随机的Int
值
func randomInt(min: Int, max:Int) -> Int { return min + Int(arc4random_uniform(UInt32(max - min + 1))) }
您遇到的崩溃是由于Swift在运行时检测到types不一致的事实。 既然Int!= UInt32,你必须先inputarc4random_uniform的input参数,然后才能计算出随机数。
如果转换的结果不适合,Swift不允许从一个整型转换为另一个整型。 例如下面的代码可以正常工作:
let x = 32 let y = UInt8(x)
为什么? 因为32是UInt8
types的int的可能值。 但是下面的代码将会失败:
let x = 332 let y = UInt8(x)
这是因为你不能把332赋给一个无符号的8位inttypes,它只能取值0到255而没有别的。
当你在C中进行转换时,int将被简单地截断,这可能是意料之外的或者是不希望的,因为程序员可能不知道截断可能发生。 所以Swift在这里处理有点不同。 只要不发生截断,它将允许这种types的转换,但如果截断,则会得到一个运行时exception。 如果你认为截断是好的,那么你必须自己去截断,让Swift知道这是有意的行为,否则Swift必须假设这是偶然的行为。
这甚至被logging( UnsignedInteger
文档):
从Swift最宽的无符号整数types转换, 陷入溢出。
而你所看到的是“溢出陷阱”,这个做法做得不好,当然,这个陷阱实际上可以解释发生了什么事情。
假设items
从来没有超过2 ^ 32个元素(有点超过40亿),下面的代码是安全的:
var i = Int(arc4random() % UInt32(items.count))
如果它可以有2 ^ 32个以上的元素,那么你会得到另一个问题,因为你需要一个不同的随机数函数来产生超过2 ^ 32的随机数。
这会自动为你创build一个随机的Int:
var i = random() % items.count
我是Inttypes的,所以不需要转换!
您可以使用
Int(rand())
为了防止应用程序启动时出现相同的随机数,可以调用srand()
srand(UInt32(NSDate().timeIntervalSinceReferenceDate)) let randomNumber: Int = Int(rand()) % items.count
这种崩溃只能在32位系统上进行。 在32位(Int32)和64位(Int64)之间的Int
更改取决于设备体系结构( 请参阅文档 )。
UInt32
的最大值是2^32 − 1
。 Int64
的最大值是2^63 − 1
,因此Int64
可以轻松处理UInt32.max
。 但是, Int32
的最大值是2^31 − 1
,这意味着UInt32
可以处理大于Int32
can的数字,并且尝试从大于2^31-1
的数字创buildInt32
将会产生溢出。
我通过尝试编译Int(UInt32.max)
行Int(UInt32.max)
来证实这一点。 在模拟器和更新的设备上,这个编译就好了。 但我连接我的旧iPod Touch(32位设备),并得到这个编译器错误:
从
UInt32
转换为Int
时,整数溢出
Xcode甚至不会为32位设备编译此行,这可能是运行时发生的崩溃。 这篇文章中的许多其他答案都是很好的解决scheme,所以我不会添加或复制这些内容。 我只是觉得这个问题没有详细解释发生了什么事情。