将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是UInt8types的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 − 1Int64的最大值是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,所以我不会添加或复制这些内容。 我只是觉得这个问题没有详细解释发生了什么事情。