Date
Nov. 21st, 2024
 
2024年 10月 21日

Post: Swift 代码规范

Swift 代码规范

Published 12:09 Sep 10, 2017.

Created by @ezra. Categorized in #Programming, and tagged as #iOS.

Source format: Markdown

Table of Content

这是什么

这是一份 Swift 代码规范和约定, 它存在的目的是:

  • 提高代码明确性, 避免意外错误
  • 减少冗余
  • 增强美感及可读性

留白和空行

  • 行首空白用 Tabs 而非 空格
  • 文件结束时留一行空行
  • 用足够的空行把代码分割成合理的块
  • 不要在一行结尾留下空白, 更不要在空行留下缩进

let/var

在意图明确的情况下尽量使用 let foo = ... 而非 var foo = ..., 因为 let 可以产生安全清晰的代码。

return/break 与 条件判断

在条件判断中, return/break 应尽早被使用, 也就是说, 你应该使用:

guard n.available else {
    return
}
// ...

而不是:

if n.available {
    // ...
} else {
    return
}

可选类型

避免对可选类型 Type? 进行不安全的强制解包, 例如:

var opt: String?
foo(opt!)

更好的做法是:

if let opt = opt {
    foo(opt)
} else {
    print("opt is nil")
}

或者, 使用可选链 (Optional Chaining):

opt?.callSomeFunctionIfOptIsNotNil()

此外如果 opt 可能为 nil, 尽可能使用 let opt: SomeType? 而非 let opt: SomeType!

只读属性与下标

如果可以, 省略只读计算属性和只读下标的 get 关键字。

也即:

var myGreatProperty: Int {
    return 4
}

subscript(index: Int) -> T {
  return objects[index]
}

… not these:

var myGreatProperty: Int {
    get {
        return 4
    }
}

subscript(index: Int) -> T {
  get {
    return objects[index]
  }
}

顶级定义与权限控制

顶级函数、类型和变量等定义声明, 应有详尽的权限控制修饰符:

public var whoopsGlobalState: Int
private func doTheThings(things: [Thing]) {}

当然, 其内部定义使用隐式的权限控制也是恰当的:

private struct TheFez {
    var owner: Person = Ezra()
}

标识符与冒号

在声明标示符的类型时, 冒号紧跟标示符, 然后空格后再写类型:

class SmallBatchSustainableFairtrade: Coffee { ... }

let timeToCoffee: NSTimeInterval = 2

func makeCoffee(type: CoffeeType) -> Coffee { ... }

对于字典类型, 冒号紧跟键类型/键值, 接着空格后再写值:

let capitals: [Country: City] = [Sweden: Stockholm]

不要出现这样的情况:

var thing : Double?
class SomeWrongStyleClass :NSObject { ... }
func drinkWine(wine:WineType) -> Void { ... }
let student:[Sring:String] = ["name"   : "Lee"]

其它符号

常见的符号、运算符 =/+/-/>/</>=/<=/&&/|| 等, 左右两端应各自保留一个空格:

let type = TypeEnum.numberType

在定义运算符/操作符时, 左右两侧也应保留一个空格:

func <| (lhs: Int, rhs: Int) -> Int
func <|< <A>(lhs: A, rhs: A) -> A

self

使用 self 更明显的区分作用域:

private class History {
    var events: [Event]

    func rewrite() {
        self.events = []
    }

  var whenVictorious: () -> () {
        return {
            self.rewrite()
        }
    }
}

struct/class

尽量选择结构体 (struct) 而非类 (class), 因为值类型更简单, 更易推断。除非, 你需要只有类才能提供的功能, 例如 identity、deinitializers 等, 但是, 通常继承并不是是使用类的理由, 因为 多态 可以通过 协议 实现, 重用 可以通过 组合 实现。

例如你有这样一份代码:

class Vehicle {
    let numberOfWheels: Int

    init(numberOfWheels: Int) {
        self.numberOfWheels = numberOfWheels
    }

    func maximumTotalTirePressure(pressurePerWheel: Float) -> Float {
        return pressurePerWheel * Float(numberOfWheels)
    }
}

class Bicycle: Vehicle {
    init() {
        super.init(numberOfWheels: 2)
    }
}

class Car: Vehicle {
    init() {
        super.init(numberOfWheels: 4)
    }
}

更好的实现方式是:

protocol Vehicle {
    var numberOfWheels: Int { get }
}

func maximumTotalTirePressure(vehicle: Vehicle, pressurePerWheel: Float) -> Float {
    return pressurePerWheel * Float(vehicle.numberOfWheels)
}

struct Bicycle: Vehicle {
    let numberOfWheels = 2
}

struct Car: Vehicle {
    let numberOfWheels = 4
}

final

类的定义应默认使用 final 关键字修饰, 并且只在需要被继承的需求明确后, 才做出改变, 但即便在这种情况下, 该类内部的定义也应尽量先使用 final 修饰, 这一规则与类相同。

类型参数

对于参数化的类型的方法, 当其接受类型的类型参数已被接受者指明时, 应当省略, 这样看起来更清晰明了。

例如, 你有这段代码

struct Composite<T> {
  ...
    func compose(other: Composite<T>) -> Composite<T> {
        return Composite<T>(self, other)
    }
}

更好的写法是:

struct Composite<T> {
    ...
    func compose(other: Composite) -> Composite {
        return Composite(self, other)
    }
}
Pinned Message
HOTODOGO
The Founder and CEO of Infeca Technology.
Developer, Designer, Blogger.
Big fan of Apple, Love of colour.
Feel free to contact me.
反曲点科技创始人和首席执行官。
开发、设计与写作皆为所长。
热爱苹果、钟情色彩。
随时恭候 垂询