每行代码长度限制

Swift 每行代码长度应该小于 100 个字符,或者说阅读的时候不应该需要滚动屏幕,在正常的实现范围里可以完整看到代码。除了以下列出的情况,否则超过这个长度应该换行。
例外的情况:

  • 如果是一串有意义的连续文本不需要换行,比如一句注释里的一个很长的 URL。

  • import 声明。

  • 生成的代码。

括号

非空的 block 花括号默认使用 K&R style。比如:

  1. while (x == y) {
  2. something()
  3. somethingelse()
  4. }

除了一些 Swift 特别要求的情况:

  • 左花括号( { )前的代码不会换行,除非超过前面提到的代码长度超过限制。

  • 左花括号后是一个换行,除非:

    • 后面要声明闭包的参数,改为在 in 关键字后面换行。

    • 符合每行只声明一件事里情况,忽略换行,把内容写在一行里。

    • 如果是空的 block ,直接声明为 { }

  • 如果右花括号( } )结束了一个声明,后面接上一个换行。比如如果右花括号后面跟的是 else ,那么后面就不会跟换行,而会写成这样 } else { 衔接。

分号

无论是终止或者隔开声明都不会使用分号。也就是说分号只可能出现在文本中。

  1. func printSum(_ a: Int, _ b: Int) {
  2. let sum = a + b
  3. print(sum)
  4. }
  1. func printSum(_ a: Int, _ b: Int) {
  2. let sum = a + b;
  3. print(sum);
  4. }

每行只声明一件事

每行最多只声明一件事,每行结尾用换行分隔。除非结尾跟的是一个总共只有一行声明的闭包

  1. guard let value = value else { return 0 }
  2. defer { file.close() }
  3. switch someEnum {
  4. case .first: return 5
  5. case .second: return 10
  6. case .third: return 20
  7. }
  8. let squares = numbers.map { $0 * $0 }
  9. var someProperty: Int {
  10. get { return otherObject.property }
  11. set { otherObject.property = newValue }
  12. }
  13. var someProperty: Int { return otherObject.somethingElse() }
  14. required init?(coder aDecoder: NSCoder) { fatalError("no coder") }

如果闭包是提前返回一个值,写在一行里可读性就会好一些。如果是一个正常的操作,可以视情况是否写在一行里。因为未来也有可能里面再增加代码的操作。

代码换行

Style guide: 代码换行

代码中的空格

除了语言或者其他样式的要求,文字和注释之外,一个Unicode空格也只出现在以下地方:

条件关键字后面和跟着的括号

  1. if (x == 0 && y == 0) || z == 0 {
  2. // ...
  3. }
  4. if(x == 0 && y == 0) || z == 0 {
  5. // ...
  6. }

如果闭包中的代码在同一行,左花括号的前面、后面,右花括号的前面有空格

  1. let nonNegativeCubes = numbers.map { $0 * $0 * $0 }.filter { $0 >= 0 }
  2. let nonNegativeCubes = numbers.map { $0 * $0 * $0 } .filter { $0 >= 0 }
  3. let nonNegativeCubes = numbers.map{$0 * $0 * $0}.filter{$0 >= 0}

在任何二元或三元运算符的两边

还有以下的情况:

  • 使用于赋值,初始化变量、属性,默认参数的等号两边。
  1. var x = 5
  2. func sum(_ numbers: [Int], initialValue: Int = 0) {
  3. // ...
  4. }
  5. var x=5
  6. func sum(_ numbers: [Int], initialValue: Int=0) {
  7. // ...
  8. }
  • 表示在协议中表示合成类型的 & 两边。
  1. func sayHappyBirthday(to person: NameProviding & AgeProviding) {
  2. // ...
  3. }
  4. func sayHappyBirthday(to person: NameProviding&AgeProviding) {
  5. // ...
  6. }
  • 自定义运算符的两边。
  1. static func == (lhs: MyType, rhs: MyType) -> Bool {
  2. // ...
  3. }
  4. static func ==(lhs: MyType, rhs: MyType) -> Bool {
  5. // ...
  6. }
  • 表示返回值的 -> 两边。
  1. func sum(_ numbers: [Int]) -> Int {
  2. // ...
  3. }
  4. func sum(_ numbers: [Int])->Int {
  5. // ...
  6. }
  • 例外:表示引用值、成员的点两边没有空格。
  1. let width = view.bounds.width
  2. let width = view . bounds . width
  • 例外:表示区域范围的 ..< 和 …两边没有空格。
  1. for number in 1...5 {
  2. // ...
  3. }
  4. let substring = string[index..<string.endIndex]
  5. for number in 1 ... 5 {
  6. // ...
  7. }
  8. let substring = string[index ..< string.endIndex]

参数列表、数组、tuple、字典里的逗号后面有一个空格

  1. let numbers = [1, 2, 3]
  2. let numbers = [1,2,3]
  3. let numbers = [1 ,2 ,3]
  4. let numbers = [1 , 2 , 3]

冒号的后面有一个空格

  1. // 类型声明
  2. struct HashTable: Collection {
  3. // ...
  4. }
  5. struct AnyEquatable<Wrapped: Equatable>: Equatable {
  6. // ...
  7. }
  8. // 参数标签
  9. let tuple: (x: Int, y: Int)
  10. func sum(_ numbers: [Int]) {
  11. // ...
  12. }
  13. // 变量声明
  14. let number: Int = 5
  15. // 字典声明
  16. var nameAgeMap: [String: Int] = []
  17. // 字典字面量
  18. let nameAgeMap = ["Ed": 40, "Timmy": 9]

代码后的注释符号 // 与代码有两个空格距离

  1. let initialFactor = 2 // Warm up the modulator.
  2. let initialFactor = 2 // Warm up the modulator.

表示字典、数组字面量的中括号外面有一个空格

  1. let numbers = [1, 2, 3]
  2. let numbers = [ 1, 2, 3 ]

禁止变量、属性水平对齐

水平对齐是明确禁止的,除非是在写明显的表格数据时,省略对齐会损害可读性。引入水平对齐后,如果添加一个新的成员可能会需要其他成员再对齐一次,这给维护增加了负担。

  1. struct DataPoint {
  2. var value: Int
  3. var primaryColor: UIColor
  4. }
  5. struct DataPoint {
  6. var value: Int
  7. var primaryColor: UIColor
  8. }

空白行(垂直方向的空白)

空白行出现在以下几种情况:

  • 两组不同类型的连续成员之间。
    以下两种情况除外:

    • 如果是两个存储属性,或者两个枚举的选项当做一个整体声明在一行里,可以不用加空行。空行可以用于这些属性间的逻辑分组。

    • 如果两个属性强相关,也可以不用空行分割。比如一个是私有的存储属性,另个一个是封装了换个私有属性的计算属性。

  • 在组织代码逻辑关系时,可以用空行隔开进行分组。

  • 第一个成员前、最后一个成员后的空行不做要求,既不鼓励,也不反对。

  • 规范里其他地方要求有空行的地方。
    使用多行空白行也可以接受,但是如果没有特别的需求不建议这样做。

括号

最顶级的 if、guard、while、switch 的条件不使用括号。

  1. if x == 0 {
  2. print("x is zero")
  3. }
  4. if (x == 0 || y == 1) && z == 2 {
  5. print("...")
  6. }
  7. if (x == 0) {
  8. print("x is zero")
  9. }
  10. if ((x == 0 || y == 1) && z == 2) {
  11. print("...")
  12. }

在有复杂的条件表达式,只有作者和 review 的人同时认为省略括号不会影响代码的可读性才会省略。不能假设每个读者都完全了解对 swift 的运算符优先级,所以这种情况下的括号提示用户的计算优先级是合理的。