每行代码长度限制
Swift 每行代码长度应该小于 100 个字符,或者说阅读的时候不应该需要滚动屏幕,在正常的实现范围里可以完整看到代码。除了以下列出的情况,否则超过这个长度应该换行。
例外的情况:
如果是一串有意义的连续文本不需要换行,比如一句注释里的一个很长的 URL。
import 声明。
生成的代码。
括号
非空的 block 花括号默认使用 K&R style。比如:
while (x == y) {
something()
somethingelse()
}
除了一些 Swift 特别要求的情况:
左花括号( { )前的代码不会换行,除非超过前面提到的代码长度超过限制。
左花括号后是一个换行,除非:
后面要声明闭包的参数,改为在 in 关键字后面换行。
符合每行只声明一件事里情况,忽略换行,把内容写在一行里。
如果是空的 block ,直接声明为
{ }
。
如果右花括号( } )结束了一个声明,后面接上一个换行。比如如果右花括号后面跟的是 else ,那么后面就不会跟换行,而会写成这样
} else {
衔接。
分号
无论是终止或者隔开声明都不会使用分号。也就是说分号只可能出现在文本中。
✅
func printSum(_ a: Int, _ b: Int) {
let sum = a + b
print(sum)
}
❌
func printSum(_ a: Int, _ b: Int) {
let sum = a + b;
print(sum);
}
每行只声明一件事
每行最多只声明一件事,每行结尾用换行分隔。除非结尾跟的是一个总共只有一行声明的闭包。
✅
guard let value = value else { return 0 }
defer { file.close() }
switch someEnum {
case .first: return 5
case .second: return 10
case .third: return 20
}
let squares = numbers.map { $0 * $0 }
var someProperty: Int {
get { return otherObject.property }
set { otherObject.property = newValue }
}
var someProperty: Int { return otherObject.somethingElse() }
required init?(coder aDecoder: NSCoder) { fatalError("no coder") }
如果闭包是提前返回一个值,写在一行里可读性就会好一些。如果是一个正常的操作,可以视情况是否写在一行里。因为未来也有可能里面再增加代码的操作。
代码换行
代码中的空格
除了语言或者其他样式的要求,文字和注释之外,一个Unicode空格也只出现在以下地方:
条件关键字后面和跟着的括号
✅
if (x == 0 && y == 0) || z == 0 {
// ...
}
❌
if(x == 0 && y == 0) || z == 0 {
// ...
}
如果闭包中的代码在同一行,左花括号的前面、后面,右花括号的前面有空格
✅
let nonNegativeCubes = numbers.map { $0 * $0 * $0 }.filter { $0 >= 0 }
❌
let nonNegativeCubes = numbers.map { $0 * $0 * $0 } .filter { $0 >= 0 }
❌
let nonNegativeCubes = numbers.map{$0 * $0 * $0}.filter{$0 >= 0}
在任何二元或三元运算符的两边
还有以下的情况:
- 使用于赋值,初始化变量、属性,默认参数的等号两边。
✅
var x = 5
func sum(_ numbers: [Int], initialValue: Int = 0) {
// ...
}
❌
var x=5
func sum(_ numbers: [Int], initialValue: Int=0) {
// ...
}
- 表示在协议中表示合成类型的 & 两边。
✅
func sayHappyBirthday(to person: NameProviding & AgeProviding) {
// ...
}
❌
func sayHappyBirthday(to person: NameProviding&AgeProviding) {
// ...
}
- 自定义运算符的两边。
✅
static func == (lhs: MyType, rhs: MyType) -> Bool {
// ...
}
❌
static func ==(lhs: MyType, rhs: MyType) -> Bool {
// ...
}
- 表示返回值的 -> 两边。
✅
func sum(_ numbers: [Int]) -> Int {
// ...
}
❌
func sum(_ numbers: [Int])->Int {
// ...
}
- 例外:表示引用值、成员的点两边没有空格。
✅
let width = view.bounds.width
❌
let width = view . bounds . width
- 例外:表示区域范围的 ..< 和 …两边没有空格。
✅
for number in 1...5 {
// ...
}
❌
let substring = string[index..<string.endIndex]
for number in 1 ... 5 {
// ...
}
❌
let substring = string[index ..< string.endIndex]
参数列表、数组、tuple、字典里的逗号后面有一个空格
✅
let numbers = [1, 2, 3]
❌
let numbers = [1,2,3]
let numbers = [1 ,2 ,3]
let numbers = [1 , 2 , 3]
冒号的后面有一个空格
✅
// 类型声明
struct HashTable: Collection {
// ...
}
struct AnyEquatable<Wrapped: Equatable>: Equatable {
// ...
}
// 参数标签
let tuple: (x: Int, y: Int)
func sum(_ numbers: [Int]) {
// ...
}
// 变量声明
let number: Int = 5
// 字典声明
var nameAgeMap: [String: Int] = []
// 字典字面量
let nameAgeMap = ["Ed": 40, "Timmy": 9]
代码后的注释符号 // 与代码有两个空格距离
✅
let initialFactor = 2 // Warm up the modulator.
❌
let initialFactor = 2 // Warm up the modulator.
表示字典、数组字面量的中括号外面有一个空格
✅
let numbers = [1, 2, 3]
❌
let numbers = [ 1, 2, 3 ]
禁止变量、属性水平对齐
水平对齐是明确禁止的,除非是在写明显的表格数据时,省略对齐会损害可读性。引入水平对齐后,如果添加一个新的成员可能会需要其他成员再对齐一次,这给维护增加了负担。
✅
struct DataPoint {
var value: Int
var primaryColor: UIColor
}
❌
struct DataPoint {
var value: Int
var primaryColor: UIColor
}
空白行(垂直方向的空白)
空白行出现在以下几种情况:
两组不同类型的连续成员之间。
以下两种情况除外:如果是两个存储属性,或者两个枚举的选项当做一个整体声明在一行里,可以不用加空行。空行可以用于这些属性间的逻辑分组。
如果两个属性强相关,也可以不用空行分割。比如一个是私有的存储属性,另个一个是封装了换个私有属性的计算属性。
在组织代码逻辑关系时,可以用空行隔开进行分组。
第一个成员前、最后一个成员后的空行不做要求,既不鼓励,也不反对。
规范里其他地方要求有空行的地方。
使用多行空白行也可以接受,但是如果没有特别的需求不建议这样做。
括号
最顶级的 if、guard、while、switch 的条件不使用括号。
✅
if x == 0 {
print("x is zero")
}
if (x == 0 || y == 1) && z == 2 {
print("...")
}
❌
if (x == 0) {
print("x is zero")
}
if ((x == 0 || y == 1) && z == 2) {
print("...")
}
在有复杂的条件表达式,只有作者和 review 的人同时认为省略括号不会影响代码的可读性才会省略。不能假设每个读者都完全了解对 swift 的运算符优先级,所以这种情况下的括号提示用户的计算优先级是合理的。