编码风格

一般规则

1. 集合转换

从一个集合转换到另一个集合时,尽量使用map、filter、reduce和其他一些类似函数式编程操作符组合,避免使用迭代的方式。

在使用这些方法时避免使用有副作用的闭包。

// ✅推荐使用
let stringOfInts = [1, 2, 3].flatMap { String($0) }

// ❌不推荐
var stringOfInts: [String] = []
for integer in [1, 2, 3] {
    stringOfInts.append(String(integer))
}
// ✅推荐使用
let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }

// ❌不推荐
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
    if integer % 2 == 0 {
        evenNumbers.append(integer)
    }
}

2. 代理弱引用

delegateprotocol中,确保生命周期,属性使用weak

3. 闭包弱引用

在闭包中避免循环引用

4. 类型推断

不要使用类方法的简写,因为从类方法比枚举推断上下文通常更困难。

5. 方法重写

在编写方法时,需要考虑是否需要重写,如果不需要,可以使用final显示的标记,这样可以提高编译时间。

6. 类方法和类属性

在声明与类关联的函数或属性时,首选是static 而不是class

当我们需要在子类中重写该函数或方法时才使用class

可以考虑使用Protocol实现这一点。

7. 计算属性

当一个函数没有参数,仅仅是返回一些对象或值,最好使用计算属性。

变量

1. letvar

尽量使用let声明变量,只有当变量确实需要改变时,才需要声明为var

2. 属性值

如果属性的值直到被创建时才知道,那么仍然可以使用let:在初始化期间分配常量属性。

3. 类型推导

如果常量或变量能够自动推导类型, 最好不要显示的声明类型。

4. 具有描述性

声明变量时,应该具有描述性,不需要太详细。

5. 避免重复

避免重复类型信息,一次就够了。

常量

1. 私有常量

在可能的情况下,与之相关的文件保持常量是私有的。

2. 定义常量

为代码中不变的数据定义常量。

例如:单元格复用标识符的字符串常量,key名称(KVC 或者 字典),segue标识符。

3. 静态常量

如果常量是在类或者结构体中声明的,则必须将其声明为static,以避免为每个实例声明一个常量。

4. 不必使用K前缀

Swift中常量不必使用K作为前缀。

5. 文件级常量

文件级常量必须使用fileprivate let声明。

元组

1. 函数返回值

如果一个函数返回多个值,可以使用命名元组。

如果返回类型很明显,可以省略命名。

如果一个元组中有3个或3个以上的项,需使用类或结构体替换。

2. 重定义

如果不止一次的使用某个元组,需要使用typealias定义。

public typealias Address = (name: String, number: Int)

访问修饰符

1. 顺序

访问修饰符必须放在声明属性之前。

2. 同一行内声明

访问修饰符关键字应该和其他代码放在一行。

3. 默认修饰符

默认修饰符为internal,无需显示的声明。

4. 单元测试

当您需要为单元测试设置一个变量为open时,将其标记为内部使用@testable import Module

如果属性应该是私有的,但是为了单元测试的目的而将其声明为内部属性,需要添加适当的文档注释。

5. 私有

尽量使用private 而不是fileprivate

6. 公开

如果在模块外部可以设置子类化的内容,尽量使用open而不是public

自定义操作符

1. 避免自定义运算符

应该避免创建自定义运算符,防止降低代码的可读性。如果需要创建自定义运算符,可以使用命名函数替换。

2. 重写现有操作符

可以覆盖现有的操作符来支持新的类型。如使用==比较时。

3. 更多参考

更多参考内容NSHipster article.

Switch 和 Enums

1. break

Switch语句中不存在隐式贯穿,因此不需要在 case 分支中显式地使用 break 语句。

2. default

当使用具有有限可能的Switch语句时,不要使用default case为未使用的case创建一个单独的用例。

3. 对齐

caseswitch左侧对齐

4. 关联值

当为枚举定义关联值时,一定要显式地描述它。

5. list case

优先使用list case (如case 1, 2, 3:)。

6. 抛出错误

当有case未执行到,需要把错误信息抛到上层而不是默认失败。

7. 避免写出枚举类型

当编译器能够自动推导出枚举类型时,则不需要写出枚举类型。

可选值

1. 避免强制解包

避免使用!,as!,try!强制解包,当强制解包空内容时会引发崩溃。

2. 可选类型

当变量,函数返回值可以为nil时,需要声明为可选类型(?)。

3. 显示比较

当需要判断值是否为nil又不需要解包出来的值,则可以进行显示比较。

4. 避免使用unowned

避免使用隐式解包

5. 使用相同名称解包

可以使用相同的变量名称解包

6. 多个可选绑定

在同一个if语句中可以使用多个可选绑定,避免多重if嵌套。

错误使用❌

正确使用✅

7. 多个可选绑定的格式

当使用ifguard对多个可选值进行解包时,将每个常量或变量放在一行,后面跟着一个逗号,,最后一行除外。

当是guard语句时,最后一行是变量后面跟着 else {,

当是if语句时,最后一行是变量后面跟着{

协议

1. 组织代码

  1. 使用// MARK:将协议与其他代码分开。

  2. classstruct的实现之外创建extension,但在同一个文件内。

#2 子类不能覆盖扩展里的方法。

2. @objc

如果协议具有可选方法,则必须使用@objc 关键字声明它。

3. 多个类使用同一协议

多个类使用同一协议,请在自己的文件内声明。

4. 仅支持class的协议

若仅支持类类型的协议,需要增加class关键字。

属性

1. 只读计算属性

创建只读计算属性,不需要增加get {}

2. 代码缩进

get {}, set {}, willSet , didSet 必须使用代码缩进。

3. newValue,oldValue

setget方法中使用标准的newValueoldValue

4. 单例的实现

必须使用下面这种方式

5. 懒加载

使用lazy关键字,实现懒加载,延迟对象的初始化,控制声明周期,在特殊情况下根据需要减少内存分配。

三目运算符

为了增加清晰度和代码整洁度,可适当使用三木运算符? :

三元运算符的最佳用途是在赋值变量和决定使用哪个值。

闭包

1. 省略类型

在闭包内声明参数,如果能够自动推导类型,也可以省略类型。

2. 行数

如果不超过一行字符的限制,则应该保持左括号和参数在同一行。

3. 简写参数

仅对简单的单行闭包实现使用简写参数语法。

对于复杂的情况,明确定义参数。

4. 尾随闭包

仅当参数列表末尾有单个闭包表达式参数时,才使用尾随闭包语法。 给出闭包参数描述性名称。

代理

1. 委托源

创建自定义代理方法时,未命名的第一个参数应该是委托源。

数组

1. 避免下标访问

避免使用下标直接访问数组,但可以使用.first.last等访问器,以确保项目不可用时的安全性。 如果没有访问器,请务必先进行正确的绑定检查。

2. 迭代

使用for item in items 语法,禁止使用for i in 0 ..< items.count

3. 连接数组

使用.append().append(contentsOf:)方法代替+-操作符去连接数组,因为在编译阶段,它们性能更高。

guard 的使用

1. 尽早退出

尽早退出是对函数进行完整性检查的首选方法。也是避免嵌套if语句的首选方法。使用guard可以提高代码的可读性。

2. 完整性检查

免使用嵌套的if语句并减少代码中嵌套缩进的数量:使用guard语句而不是if语句进行完整性检查。

3. 避免单行guard语句

避免在一行上使用guard语句。

4. 使用范围

只有在失败导致退出当前上下文时才应使用guard; 如果上下文需要继续则应该使用一个或多个if语句。 guard的目标是进行早期检查和返回。

5. 状态选择

如果在两个不同的状态之间进行选择,则使用if语句而不是使用guard语句更有意义。

最后更新于

这有帮助吗?