Swift 代码规范

摘要:
在团队开发过程中,每个人的代码风格都不一样,编写代码规范的目标是为了保证代码的简洁性,可读性和可维护性,提高工作效率。下面是对命名、注释、空格、风格、函数声明、分号等等编写了相应的规范。

Swift 代码规范

命名

  • PascalCase使用驼峰命名规则和描述性的名称来定义类、方法、变量等。类名和模块中的常量的第一个字母应该大写,而方法名和变量应该开始用小写字母。这一点跟OC一样的命名规则

推荐

1
2
3
4
class UserInfo{
var userName: String
var userAge: String
}

不推荐

1
2
3
4
class app_userInfo{
var uName: String
var uAge: String
}
  • camelCase使用小骆驼拼写法 (首字母小写) 为函数,方法,变量,常量,参数等命名。

  • 在实际使用过程中,对于函数和int函数,需对所有参数进行命名,除非上下文非常清清楚,是函数调节具有可读性。
    例子:

1
func updateUserInfo(userName: String, userAge: Int)
  • 首字母缩略词在命名中一般来说都是全部大写,例外的情形是如果首字母缩略词是一个命名的开始部分,而这个命名需要小写字母作为开头,这种情形下首字母缩略词全部小写。

    1
    2
    3
    4
    5
    6
    7
    8
    // "HTML" is at the start of a constant name, so we use lowercase "html"
    let htmlBodyContent: String = "<p>Hello, World!</p>"
    // Prefer using ID to Id
    let profileID: Int = 1
    // Prefer URLFinder to UrlFinder
    class URLFinder {
    /* ... */
    }
  • 使用前缀 k + 大骆驼命名法 为所有非单例的静态常量命名。
  • 不要缩写,简写命名,或用单个字母命名。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 推荐
    class RoundAnimatingButton: UIButton {
    let animationDuration: NSTimeInterval
    func startAnimating() {
    let firstSubview = subviews.first
    }
    }
    // 不推荐
    class RoundAnimating: UIButton {
    let aniDur: NSTimeInterval
    func srtAnmating() {
    let v = subviews.first
    }
    }

格式

在团队开发过程中,代码格式非常重要,保持良好的代码编写习惯。

  • 每个文件结尾后又空白行。
  • 每一行都不能以空白作为结尾。
  • 左边的大括号不用另起一行。
1
2
3
4
5
6
7
8
9
10
11
12
class PPClass {
func ppMethod(){
if x == y {
/* .... */
} else if x == z {
/* .... */
} else {
/* .... */
}
}
/* .... */
}
  • 在写一个变量、一个字典、一个函数等等的时候,在:在不加空格,在:后面加空格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 指定类型
let age: Int = 27
// 字典语法(注意这里是向左对齐而不是分号对齐)
let userInfo: [String: AnyObject] = [
"userName": "Austin",
"age": 27]
// 声明函数
func myFunction<t, u: someprotocol where t.relatedtype == u>(firstArgument: U, secondArgument: T) {
    /* ... */
}
// 调用函数
someFunction(someArgument: "Austin")
// 父类
class CustomViewController: UIViewController {
/* ... */
}
// 协议
extension PirateViewController: UITableViewDataSource {
/* ... */
}</t, u: someprotocol where t.relatedtype == u>
  • 基本上,要在,后面加空格
1
let array = [1, 2, 3, 4, 5]
  • 在做二元运算符的时候前后都要需要添加空格,有时候可能读不出来。
1
2
3
4
5
6
7
let sum = 20 + (30 / 2) * 3
if 2 + 3 == 5 {
fatalError("The universe is broken.")
}
func pancake() -> Pancake {
/* ... */
}
  • 当调用的函数有多个参数时,每一个参数另起一行,并比函数名多一个缩进。
1
2
3
4
someFunction(
first: "Hello, I am Austin",
second: resultFromSomeFunction()
third: someOtherLocalVariable)
  • 如果要进行多条件一起判断,尽量使用本地变量或者其他方式,增强代码可读性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 推荐
    let firstCondition = x == firstReallyReallyLongPredicateFunction()
    let secondCondition = y == secondReallyReallyLongPredicateFunction()
    let thirdCondition = z == thirdReallyReallyLongPredicateFunction()
    if firstCondition && secondCondition && thirdCondition {
    // 你要干什么
    }
    // 不推荐
    if x == firstReallyReallyLongPredicateFunction()
    && y == secondReallyReallyLongPredicateFunction()
    && z == thirdReallyReallyLongPredicateFunction() {
    // 你要干什么
    }

风格

  • 能用letlet,尽量少用var
  • 在对数组或者集合进行操作的时候,尽量使用高级函数map,filter,reduce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 推荐
let stringOfInts = [1, 2, 3].flatMap { String($0) }
// ["1", "2", "3"]
// 不推荐
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 }
// [4, 8, 16, 42]
// 不推荐
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
if integer % 2 == 0 {
evenNumbers(integer)
}
}
  • 如果一个函数有多个返回值,推荐使用 元组 而不是 inout 参数, 如果你见到一个元组多次,建议使用typealias ,而如果返回的元组有三个或多于三个以上的元素,建议使用结构体或类。
1
2
3
4
5
6
func UserName() -> (firstName: String, lastName: String) {
return ("Guybrush", "Threepwood")
}
let name = UserName()
let firstName = name.firstName
let lastName = name.lastName
  • 当使用委托和协议时,请注意避免出现循环引用,基本上是在定义属性的时候使用 weak 修饰,在闭包里使用 self 的时候要注意出现循环引用,使用捕获列表可以避免这一点。
1
2
3
4
5
6
7
8
9
myFunctionWithClosure() { [weak self] (error) -> Void in
// 方案 1
self?.doSomething()
// 或方案 2
guard let strongSelf = self else {
return
}
strongSelf.doSomething()
}
  • 使用if做判断的时候,不使用()
1
2
3
4
5
6
7
8
// 推荐
if x == y {
/* ... */
}
// 不推荐
if (x == y) {
/* ... */
}
  • 写枚举的时候,请简写
1
2
3
4
// 推荐
imageView.setImageWithURL(url, type: .person)
// 不推荐
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)
  • 在使用类方法的时候不用简写,因为类方法不如 枚举 类型一样,可以根据轻易地推导出上下文。
1
2
3
4
// 推荐
imageView.backgroundColor = UIColor.whiteColor
// 不推荐
imageView.backgroundColor = .whiteColor
  • 不建议使用用self.修饰除非需要。

  • 访问修饰符不应单独另起一行,应和访问修饰符描述的对象保持在同一行。

1
2
3
4
5
6
7
8
9
// 推荐
public class Pirate {
/* ... */
}
// 不推荐
public
class Pirate {
/* ... */
}
  • 自定义操作符

不推荐使用自定义操作符,如果需要创建函数来替代。

在重写操作符之前,请慎重考虑是否有充分的理由一定要在全局范围内创建新的操作符,而不是使用其他策略。

你可以重载现有的操作符来支持新的类型(特别是 ==),但是新定义的必须保留操作符的原来含义,比如 == 必须用来测试是否相等并返回布尔值。

  • 不要使用 as! 或 try!。
  • 如果对于一个变量你不打算声明为可选类型,但当需要检查变量值是否为 nil,推荐用当前值和 nil 直接比较,而不推荐使用 if let 语法。
1
2
3
4
5
6
7
8
// 推荐
if someOptional != nil {
// 你要做什么
}
// 不推荐
if let _ = someOptional {
// 你要做什么
}
  • 在实现协议的时候,有两种方式来组织你的代码:
    使用 // MARK: 注释来分割协议实现和其他代码。
    使用 extension 在 类/结构体已有代码外,但在同一个文件内。

  • 使用 guard 语句
    总体上,我们推荐使用提前返回的策略,而不是 if 语句的嵌套。使用 guard 语句可以改善代码的可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 推荐
func eatDoughnut(atIndex index: Int) {
guard index >= 0 && index < doughnuts else {
// 如果 index 超出允许范围,提前返回。
return
}
let doughnut = doughnuts[index]
eat(doughnut)
}
// 不推荐
func eatDoughnuts(atIndex index: Int) {
if index >= 0 && index < donuts.count {
let doughnut = doughnuts[index]
eat(doughnut)
}
}

如果需要在2个状态间做出选择,建议使用if 语句,而不是使用 guard 语句。

1
2
3
4
5
6
7
8
9
10
11
12
// 推荐
if isFriendly {
print("你好, 远路来的朋友!")
} else {
print(“穷小子,哪儿来的?")
}
// 不推荐
guard isFriendly else {
print("穷小子,哪儿来的?")
return
}
print("你好, 远路来的朋友!")

文档与注释

如果一个函数比较复杂,通常需要给函数添加注释文档。使用苹果标准的方式进行注册 option+command+/进行注释
请务必查看在Apple文档中描述的Swift的注释标记中提供的全部功能。

指南:

    1. 160字符列限制
    1. 即使注释只占用一行,请使用/** */
    1. 不要再每一行附加一个*
    1. 确定使用新的 – parameter格式,而不是就得Use the new -:param: 格式,另外注意 parameter 是小写的。
1
2
3
4
5
6
7
8
9
10
11
12
class Human {
/**
This method feeds a certain food to a person.
- parameter food: The food you want to be eaten.
- parameter person: The person who should eat the food.
- returns: True if the food was eaten by the person; false otherwise.
*/
func feed(_ food: Food, to person: Human) -> Bool {
// ...
}
}
    1. 如果需要给一个方法的 参数/返回值/抛出异常 添加注释,务必给所有的添加注释,即使会看起来有部分重复,否则注释会看起来不完整,有时候如果只有一个参数值得添加注释,可以在方法注释里重点描述。
    1. 对于复杂的类,可以使用一些潜在的例子来描述类的用法。请记住,Swift的注释文档中的markdown语法是有效的。换行符,列表等是适当的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
## Feature Support
This class does some awesome things. It supports:
- Feature 1
- Feature 2
- Feature 3
## Examples
Here is an example use case indented by four spaces because that indicates a
code block:
let myAwesomeThing = MyAwesomeClass()
myAwesomeThing.makeMoney()
## Warnings
There are some things you should be careful of:
1. Thing one
2. Thing two
3. Thing three
*/
class MyAwesomeClass {
/* ... */
}
    1. 在写文档注释时,尽量保持简洁。当提到代码时,使用 -
1
2
3
4
5
6
7
/ **
这样做会有一个“UIViewController”,有时间。
- 警告:在运行此函数之前,请确保`someValue`为`true`。
* /
func myFunction() {
/ * ... * /
}
    1. // 后面要保留空格。
    1. 注释必须要另起一行。
    1. 使用注释 // MARK: - xoxo 时, 下面一行保留为空行。
1
2
3
4
5
6
7
8
9
10
11
12
class Pirate{
// MARK: - 实例属性
private let pirateName : String
// MARK: - 初始化
init() {
/ * ... * /
}
}