参考项目实际、官方文档、raywenderlich(传送门)等大神总结的swift语言的编码规范,适应目前swift 4.2,笔者会不定期更新,欢迎指正补充
约定,请尽量确保代码编译不残留warning,这有可以规避很多问题
文章目录
命名规范
常量,变量,函数,方法的命名规则使用小驼峰规则,首字母小写;
类别名称(类、结构体、枚举和协议)使用大驼峰规则,首字母大写。
正例
let maximumWidgetCount = 100
class WidgetContainer {
var widgetButton: UIButton
let widgetHeightPercentage = 0.85
}
反例
let MAX_WIDGET_COUNT = 100
class app_widgetContainer {
var wBut: UIButton
let wHeightPct = 0.85
}
缩写
缩写和简写只能使用常用的或者约定俗成的缩写,缩写和简写中的所有字符的大小写要一致
正例
let urlString: URLString
let userID: UserID
反例
let uRLString: UrlString
let userId: UserId
bool类型变量命名时,建议以is作为前缀
正例
var isMine: Bool = false
代理
在创建自定义代理方法时,第一个未命名的参数应该是代理源
正例
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
反例
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
使用类型推断的上下文
推荐使用编译器推断的上下文来编写更加简短清晰的代码。不作为规范强制。
正例
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
反例
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
使用懒加载来细致地控制对象的生命周期,对于想实现延迟加载视图的UIViewController特别有用
// MARK: - 懒加载
private lazy var tableView: UITableView = {
let tableView = UITableView.init(frame: .zero, stype: .plain)
tableView.separatorStyle = .none
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: homeListCell, bundle: nil), forCellReuseIdentifier: homeListCell)
return tableView
}()
语法规范
最少化import
仅导入所需要的模块,例如,当导入Foundation能满足功能时不要导入UIKit;同理,当导入UIKit后,不要再导入Foundation
正例
import UIKit
var view: UIView
var deviceModels: [String]
import Foundation
var deviceModels: [String]
反例
import UIKit
import Foundation
var view: UIView
var deviceModels: [String]
import UIKit
var deviceModels: [String]
可选类型拆包取值时,先使用if let判断
if let data = result.data {
// do someting
}
多个可选类型拆包取值时,将多个if let 判断合并
if let name = persion.name, let age = person.age {
// do something
}
尽量不要使用 as! 或 try! ,对于可选类型Optional多使用as? ,?? 可以给变量设置默认值
// 使用if let as? 判断
if let name = person.name as? String {
// doSomething
}
// 给name变量设置默认值
var name = person.name ?? ""
数组和字典变量定义时需要标明泛型类型,并使用更简洁清晰的语法
var names: [String] = []
var values: [String: Int] = [:]
var person: [String: Any] = [:]
常量定义,建议尽可能定义在类型里面,避免污染全局命名空间,如果是其他地方有可能复用的可以定义在类型外面
static let homeListCell = "HomeListCell"
class HomeListCell: UITableViewCell {
static let kHomeCellHeight = 80.0
}
优先使用guard来确保条件判断的简短
func login(with username: String?, password: String?) throws -> LoginError {
guard let username = username else {
throw .noUsername
}
guard let password = password else {
throw .noPassword
}
// doSomething
}
使用for in表达式进行遍历
for index in (0...10) {
print(index)
}
当接口版本不兼容时,使用@available(iOS x.0, *)来表明接口适配的起始系统版本
@available(iOS 8.0, *)
func myFunction() {
// doSomething
}
编码格式
语句结束使用回车符,不使用分号
使用二元运算符(+ - * / = == > <等)的前后都需要添加空格
let value = 1 + 2
逗号后面加一个空格
let titleArray = [1, 2, 3, 4, 5]
缩进
方法、if、switch等左大括号不要另起一行,跟随语句放在行末,前置1空格;右大括号独占一行,除非后面跟着统一语句的剩余部分(do while、if else等)
func myFunction {
if ... {
...
} else {
...
}
}
判断语句不用加括号
if typeValue == 1 {
...
}
尽量不适用self,除非形参与属性同名
func setPerson(name: String, pAge: Int) {
self.name = name
age = pAge
}
访问枚举类型时,使用更简洁的点语法
enum Direction {
case north
case south
case east
case west
}
let currentDerection = .east
应删除未使用的代码,包括Xcode末班代码和占位符注释,只实现超类的代理方法等(如未使用的UIApplicationDelegate方法)
正例
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}
反例
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return Database.contacts.count
}
注释
一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。
注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。
注释内容要简洁、明了、无二义性,信息全面且不冗余。
注释跟代码一样重要。
写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。
修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。
推荐使用// MARK: - ,按功能、协议、代理等分组
// MARK: - UITableViewDelegate
// MARK: - Action
// MARK: - Request
文件头注释必须包含版权许可、功能说明、作者和创建日期
函数头注释
推荐使用Xcode注释快捷键(⌘⌥/)
/// <#Description#>
///
/// - Parameters:
/// - admin: <#admin description#>
/// - passWord: <#passWord description#>
func registHilinkGateWay(admin : String , passWord : String) -> Void {
}
代码注释
代码注释放于对应代码的上方或者右边
注释符与注释内容间空1格;右置注释与前面代码空1格;代码上方的注释,应与对应代码保持一样的缩进。
// 单行注释
doSomething()
// 多行注释
// 第二行
doSomething()
doSomething() // 右置注释
函数
函数设计
避免函数过长,建议函数不超过50行(去空行去注释)
避免函数的代码块嵌套过深,建议不要超过4层
函数的代码块嵌套深度指的是函数中的代码控制块(如:if、for、while、switch等)之间互相包含的深度。
推荐使用卫语句来减少if相关的嵌套层级
优化前
func handleReceive(receive: String) {
if receive {
let type = receive.type
if type != UNKNOWN {
// doSomething
}
}
}
优化后
func handleReceive(receive: String) {
if !receive { // 使用'卫语句'
return
}
let type = receive.type
if type != UNKNOWN {
// doSomething
}
}
函数的参数建议不超过5个
闭包
为避免循环引用,闭包内使用弱引用;为避免弱引用被提前释放,多次引用前使用强引用转换。
resource.request().onComplete {[weak self] response in
guard let strongSelf = self else { return } // 强引用显式地延长self的生命周期
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
resource.request().onComplete {[weak self] response in
guard let `self` = self else { return } // 推荐使用swift语法糖``
let model = self.updateModel(response)
self.updateUI(model)
}
当闭包时函数的最后一个参数,采用尾随闭包写法
UIView.animateWithDuration(1.0) {
self.myView.alpha = 0
}
当单个闭包表达式上下文清晰时,使用隐式的返回值
arrayList.sort { a,b in
a > b
}