随机数的生成一直是程序员面临的一个大问题,在由CPU 时钟
、进程
和线程
所构建出的世界中,是没有
真正的随机
的。在给定一个随机种子后,使用某些神奇的算法我们可以得到一组随机的序列
。
arc4random
是一个非常优秀的随机数算法,并且在Swift 中也可以使用。它会返回给我们一个任意整数,我们想要某个范围里的数的话,做模运算(%
)取余数就行了。
uint32_t arc4random(void);
但在使用arc4random 函数
的时候,有时候程序会崩溃,请注意这个“有时候”
最让程序员郁闷的事情莫过于程序有时候会崩溃
而有时候又能良好运行
。还好这里的情况比较简单,聪明的我们马上就能指出原因。其实Swift 的Int
是和CPU 架构有关
的:在32 位的CPU 上,它实际上是Int32
,而在64 为的CPU 上是Int64
。arc4random
所返回的值不论在什么平台上都是一个UInt32
,于是在32 位的平台上就有一半的概率在进行Int 转换
时越界
,时不时出现的崩溃
也就不足为奇了。
这种情况下,一种相对安全的做法是使用一个arc4random
的改良版本
:
uint32_t
arc4random_uniform(uint32_t __upper_bound) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
这个改良版本接受一个UInt32
的数字n
作为输入,将结果归化到0
至 n - 1
之间。只要我们的输入不超过Int 的范围,就可以避免危险的转换:
let diceFacecount: UInt32 = 6
let randomRoll = Int(arc4random_uniform(diceFacecount)) + 1
print(randomRoll)
最佳实践当然是为Range
创建一个生成随机数的方法的随机数方法
,这样我们就能在之后很容易的复用,甚至设计类似Randomable
这样的接口了:
func randomInRange(_ range: Range<Int>) -> Int {
let count = UInt32(range.endIndex - range.startIndex)
return Int(arc4random_uniform(count)) + range.startIndex
}
for _ in 0...100 {
print(randomInRange(1..<6))
}