前言
在去年swift3.0发布了,新版本可以在Xcode 8中使用了,或者你可以直接从swift.org下载编译器。
从代码可读性来看,新版本有很多提升,函数调用的连续性,更好的命名约定和移除了部分c风格的元素。
从代码可读性来来看,NS前缀已经从Foundation类型中移除,例如NSBundle.mainBundle()
现在改为Bundle.mainBundle()
.
c风格的一元操作符++和--在3.0中已经不适用了:
// Only in Swift 2.2 and earlier var number = 10 number++ ++number number-- --number</div>
相对应的表达方式是number += 1 or number -= 1
.
另外一个有趣的变化是移除了c风格的for-loop,我记得在学校中(使用c语言)写这种循环:
// Only in Swift 2.2 and earlier let steps = 5 for var step = 0; step < steps; step++ { print(step) } // 0 1 2 3 4</div>
主要的原因是存在了更好的对应写循环的方法for-in
和stride().for-loop
在理解上比较困难和缺少swift风格。当新的方法出现后,for-loop
已经很少在代码库中使用了。
这篇文章将讲解典型的for-loop
使用场景,同时说明迁移到 for-in
, stride()
或者简单的while() {}
.
一、如何迁移 for-loop 到 for-in
for-loop
应用的典型场景在一个数字区间内迭代。这些数字可以是数组的索引等.
例如,让我们遍历数组的每一个元素:
// Only in Swift 2.3 and earlier let birds = ["pigeon", "sparrow", "titmouse"] for var index = 0; index < birds.count; index++ { print(birds[index]) } // "pigeon" "sparrow" "titmouse"</div>
可见,let index = 0; index < birds.count; index++
的循环部分是冗长的。许多元素是多余的,整个表达式可以简化的。替换为手动的增量,整个操作可以用更具表达性的语法来自动化。
for-in
循环更简短和更具表达性。让我们迁移上面的代码:
let birds = ['pigeon', 'sparrow', 'titmouse'] for index in 0..<birds.count { print(birds[index]) } // 'pigeon', 'sparrow', 'titmouse'</div>
现在感觉好很多了。index in 0..<birds.count
更容易阅读和理解。0..<birds.count
这部分定义一个半开区间的 Range 类型。for-in
循环迭代0,1和2的范围值(不包括上限3)。
这不是全部! 你甚至可以跳过索引并直接访问数组元素:
let birds = ["pigeon", "sparrow", "titmouse"] for bird in birds { print(bird) } // "pigeon" "sparrow" "titmouse"</div>
可以看出,对于标准数组或集合迭代for-in
对于for-loop
是一个更好的替代。 至少在这种情况下,在Swift 3.0中删除for-lo
op的决定是合理的。
二、如何迁移 for-loop 到 stride
你可以合理地要求for-loop
虽然是冗长的,但仍然是灵活的。 它对于更复杂的迭代是有用的。
让我们尝试一个场景。你要打印一个具有奇数索引元素的元素数组。一个 for-loop
可能看起来像这样:
// Only in Swift 2.3 and earlier let colors = ["blue", "green", "red", "white", "black"] for var index = 0; index < colors.count; index += 2 { print(colors[index]) } // => "blue" "red" "black"</div>
由于索引根据表达式 index += 2
而每次增加2,所以只有奇数索引的元素会被显示:”blue”, “red” and “black”.
你可以尝试使用 for-in
并定义一个范围。但是需要奇数索引加上附加的验证:
let colors = ["blue", "green", "red", "white", "black"] for index in 0..<colors.count { if (index % 2 == 0) { print(colors[index]) } } // => "blue" "red" "black"</div>
的确, if (index % 2 == 0) { ... }
条件句在这里看起来怎么样。
这种情况很符合使用 Swift 的stride(from: value, to: value, by: value)
函数。定义开始,结束(不包括上限)和步长值,函数返回相应的数字序列。
让我们在我们的场景中应用stride:
let colors = ["blue", "green", "red", "white", "black"] for index in stride(from: 0, to: colors.count, by: 2) { print(colors[index]) } // => "blue" "red" "black"</div>
stride(from: 0, to: colors.count, by: 2)
返回以0开始到5的数字(上限不包含5),步长为2。对于 for-loop
,这是一个好的替代。
如果上限必须包含进来,这里有另外一种函数格式:
stride(from: value, through: value, by: value)
。第二个参数的标签是 through, 这个标签是用以指明是包含上限的。
三、其他情况坚持使用while
c风格for-loop
的每个组件都有一个很好的属性:初始化,跳出严重和完全可配置的增量:
for <initialization>; <verification>; <increment> { // loop body }</div>
此外,你可以省略其中的任何组件,要是你能在for-loop
的循环块打破循环。
例如,让我们打印一个数字数组的元素,直到数字0被遇到。可以使用C风格的for-loop
:
// Only in Swift 2.2 and earlier let numbers = [1, 6, 2, 0, 7], nCount = numbers.count for var index = 0; index < nCount && numbers[index] != 0; index++ { print(numbers[index]) } // => 1 6 2</div>
验证部分 index < nCount && numbers[index] != 0
是用以检查是否0在数组中出现。如果出现,则跳出循环。
所以只有0之前的数字被打印出来:1,6和2。
for var index in 0..<nCount
是一个迁移选项。你只是需要使用条件 if numbers[index] == 0
,当0出现的时候跳出循环:
let numbers = [1, 6, 2, 0, 7], nCount = numbers.count for index in 0..<nCount { if (numbers[index] == 0) { break } print(numbers[index]) } // => 1 6 2</div>
但 break 语句出现,它会轻微减少阅读流程。但是我想要容易地阅读代码流程!
while(<condition>) {...}
循环可能是一个更好的替代方案。让我们看看上一个例子是如果被修改的:
let numbers = [1, 6, 2, 0, 7], nCount = numbers.count var index = 0 while (index < nCount && numbers[index] != 0) { print(numbers[index]) index += 1 } // => 1 6 2</div>
如果你有的情况无法使用 for-in 或者 stride()
, 那么我推荐你使用 while(){}
。
四、统一参数标签行为
在Swift 2.2 和更早版本你可以在调用函数的时候忽略第一个参数的标签:
// Only in Swift 2.2 and earlier func sum(firstItem: Int, secondItem: Int) -> Int { return firstItem + secondItem } sum(5, secondItem: 2) // => 7</div>
对于我来说,这个忽略的做法给我带来困扰。你不得不忽略第一个参数的标签,然而剩下的参数却还保持有标签。这是一种不自然的规则。
幸运的是从3.0版本开始,所有参数将强制拥有标签。
让我们来迁移上一个例子:
func sum(firstItem: Int, secondItem: Int) -> Int { return