找不到超载" init'接受提供的参数

时间:2015-01-09 14:13:31

标签: ios swift int arc4random uint32

var x = jokes[arc4random_uniform(UInt32(jokes.count))]

为什么这行代码会产生这样的错误?

编写此代码时

var x = jokes[Int(arc4random()%jokes.count)]

出现此错误

Int is not convertible to UInt32

1 个答案:

答案 0 :(得分:3)

数组下标需要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返回UInt32jokes.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对整数转换很挑剔的原因。当你转换它时,你需要绝对确定它是一个安全的转换。你不应该只是将它们洒在它周围,直到它编译。<​​/ p>

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

再补充一点,您会注意到randomValueLessThan()中的数字转换会产生许多可能的无效情况。如果你传递一个小于0的数字,你就会崩溃(但这不应该是令人惊讶的)。如果你传递的数字大于UInt32.Max,你也会崩溃,这会更令人惊讶,但在大多数代码中都不太可能。这一点是通过添加这些演员表,我们已经randomValueLessThan一个部分函数。它没有在其所有输入范围内定义(它是“域”)。在“现实生活”编程中,我们一直这样做,我们只希望它永远不会咬我们。但是当你打破类型安全时,斯威夫特试图通过让它变得更加明显来帮助我们减少伤害。

uniformSelection有类似的问题。它仅为具有少于UInt32.Max个元素的数组定义。这些有时会感觉像无意义的角落情况,并且它们是,直到突然他们不是,你的程序崩溃。 (Array.subscript也是一个部分函数,​​因为它未定义数组范围之外的值。我实际上向Apple建议Array.subscript返回一个可选项来解释它。他们可能明智地忽略我。 )