找不到接受提供的参数的重载“init”

Could not find an overload "init' that accept the supplied argument

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 return UInt32jokes.count() return Int。你不能模数不同的类型。你需要让他们到同一个地方。好吧,我们想要一个 Int,对吧?看起来很简单:

let n = Int(arc4random()) % jokes.count   // WARNING!!!! Never ever do this!!!!

为什么 Apple 如此迂腐,逼着我们手工去做?编译器不能自动转换它吗?好吧,上面的代码在 64 位处理器上可以正常工作,但在 32 位处理器上大约有一半的时间会崩溃。那是因为通过调用 Int(),您承诺该值将 始终 Int 范围内。在 32 位处理器上,这是 32 位有符号范围。但是 arc4random return 是整个 32 位 无符号 范围内的一个值,其中包括许多不适合 Int 的数字。责备! (或者,如果您关闭边界检查,那么它只会像在 C 中那样丢弃您的数字,这也好不到哪儿去。)

这就是 Swift 对整数转换很挑剔的原因。转换时您需要绝对确定它是安全的转换。在编译之前,你不应该只是把它们洒在周围。

也就是说,当然你不应该在 arc4random 上使用模数。 But that's a different question.

作为补充说明,您会注意到 randomValueLessThan() 中的数字转换会造成许多可能的无效情况。如果你传递一个小于 0 的数字,你就会崩溃(不过这并不奇怪)。如果你传递一个大于 UInt32.Max 的数字,你也会崩溃,这有点令人惊讶,但在大多数代码中不太可能。关键是通过添加这些转换,我们使 randomValueLessThan 成为 部分函数 。它没有在其所有输入范围内定义(它是 "domain")。在 "real life" 编程中,我们一直这样做,我们只是希望它永远不会咬我们。但是 Swift 试图通过在您破坏类型安全时使它更加明显来帮助我们减少被咬。

uniformSelection也有类似的问题。它仅针对元素少于 UInt32.Max 的数组定义。这些有时感觉像是无意义的极端情况,它们确实如此,直到它们突然消失并且您的程序崩溃了。 (Array.subscript 也是一个部分函数,​​因为它对于数组范围之外的值是未定义的。我实际上向 Apple 建议 Array.subscript return 一个可选的来解释这一点。他们可能是明智的不理我。)