var x = jokes[arc4random_uniform(UInt32(jokes.count))]
为什么这行代码会产生这样的错误?
编写此代码时
var x = jokes[Int(arc4random()%jokes.count)]
出现这个错误
Int is not convertible to UInt32
var x = jokes[arc4random_uniform(UInt32(jokes.count))]
为什么这行代码会产生这样的错误?
编写此代码时
var x = jokes[Int(arc4random()%jokes.count)]
出现这个错误
Int is not convertible to UInt32
数组下标采用Int
,而不是UInt32
。您需要转换回来:
let x = jokes[Int(arc4random_uniform(UInt32(jokes.count)))]
如果这对您来说有点吵,您可能需要创建一个函数来处理它:
func randomValueLessThan(x: Int) -> Int {
return Int(arc4random_uniform(UInt32(x)))
}
或者您可以扩展 Array 以提供帮助:
extension Array {
func uniformSelection() -> T {
return self[Int(arc4random_uniform(UInt32(self.count)))]
}
}
编辑:值得深入研究一下这个Int(arc4random()%jokes.count)
案例,因为你可能会试图错误地修复它,它说明了为什么 Swift 会以它的方式工作。
让我们从你的版本开始
let n = Int(arc4random() % jokes.count)
// => Could not find an overload for 'init' that accepts the supplied arguments
这有点令人困惑。让我们简化一下看看问题
let n = arc4random() % jokes.count
// => Cannot invoke '%' with an argument list of type '(UInt32, Int)'
那应该更清楚。arc4random
返回一个UInt32
并jokes.count()
返回一个Int
。你不能对不同的类型进行模数。你需要把他们带到同一个地方。好吧,我们想要一个Int
,对吧?似乎很容易:
let n = Int(arc4random()) % jokes.count // WARNING!!!! Never ever do this!!!!
为什么 Apple 如此迂腐,并迫使我们手工去做?编译器不能自动转换它吗?好吧,上面的代码在 64 位处理器上可以正常工作,而在 32 位处理器上大约有一半的时间会崩溃。那是因为通过调用Int()
,您承诺该值将始终在该Int
范围内。在 32 位处理器上,这就是 32 位有符号范围。但arc4random
返回整个 32 位无符号范围内的值,其中包括许多不适合Int
. 砰!(或者,如果您关闭边界检查,那么它只会像在 C 中那样丢弃您的数字,这也不是更好。)
这就是为什么 Swift 对整数转换很挑剔的原因。当您转换时,您需要绝对确定这是一个安全的转换。在编译之前,您不应该只是将它们撒在周围。
也就是说,你当然不应该在arc4random
. 但这是一个不同的问题。
作为补充说明,您会注意到数字转换randomValueLessThan()
会产生许多可能的无效情况。如果你传递一个小于 0 的数字,你会崩溃(不过这并不奇怪)。如果你传递一个大于 的数字UInt32.Max
,你也会崩溃,这有点令人惊讶,但在大多数代码中不太可能发生。这样做的重点是,通过添加这些强制转换,我们创建了randomValueLessThan
一个偏函数。它没有在其所有输入范围内定义(它是“域”)。在“现实生活”编程中,我们一直这样做,我们只是希望它永远不会咬我们。但是 Swift 试图通过在你破坏类型安全时让它更加明显来帮助我们减少被咬的次数。
uniformSelection
有类似的问题。它仅针对元素少于UInt32.Max
元素的数组定义。这些有时感觉像是毫无意义的极端情况,它们确实如此,直到突然它们不是并且你的程序崩溃了。(Array.subscript
也是一个部分函数,因为它对于数组范围之外的值是未定义的。我实际上建议苹果Array.subscript
返回一个可选的来解释这一点。他们可能明智地忽略我。)