Swift编码规范

作者: eaa1d39d22a8 | 来源:发表于2017-02-15 18:47 被阅读1018次

    包括格式规范、命名规范、语法规范,Objective-C兼容四个部分。基于Swift release 3.0.2版本。由于目前Swift还在快速迭代中,此文档后续也会保持同步更新。


    1. 格式规范

    1.1 使用4个空格进行缩进

    1.2 二元运算符(+, ==, 或->)的前后都需要添加空格

    推荐

    let testValue = 1 + 2
                        
    if testValue == 1 {
        /* ... */
    }
                        
    func testFunction(with testValue: TestClass) -> returnValue {
        /* ... */
    }
    

    1.3 一般情况下,在逗号后面加一个空格

    推荐

    let testArray = [1, 2, 3, 4, 5]
    

    不推荐

    let testArray = [1,2,3,4,5]
    

    1.4 每个文件结尾留一行空行

    1.5 代码结尾不要使用分号;

    推荐

    print("Hello World")
    

    不推荐

    print("Hello World");
    

    1.6 左大括号不要另起一行

    推荐

    // 1. Class Define
    class TestClass {
        /* ... */
    }
    
    // 2. if
    if testValue == 1 {
        /* ... */
    }
    
    // 3. if else
    if testValue == 1 {
        /* ... */
    } else {
        /* ... */
    }
    
    // 4. while
    while isTrue {
        /* ... */
    }
    
    // 5. guard
    guard let testValue = testValue else  {
        /* ... */
    }
    

    不推荐

    // 1. Class Define
    class TestClass 
    {
        /* ... */
    }
    
    // 2. if
    if testValue == 1
    {
        /* ... */
    }
    
    // 3. if else
    if testValue == 1
    {
        /* ... */
    }
    else
    {
        /* ... */
    }
    
    // 4. while
    while isTrue 
    {
        /* ... */
    }
    
    // 5. guard
    guard let testValue = testValue else 
    {
        /* ... */
    }
    

    1.7 判断语句不用括号

    推荐

    if testValue == 1 {
        /* ... */
    }
    
    if testValue == 1 && testString == "test" {
        /* ... */
    }
    

    不推荐

    if (testValue == 1) {
       /* ... */
    }
    
    if ((testValue == 1) && (testString == "test")) {
       /* ... */
    }
    

    1.8 不建议使用self. ,除非方法参数与属性同名

    使用self.情况

    func setText(text: String) {
        self.text = test
    }
    

    1.9 枚举类型的访问使用更简洁的点语法

    推荐

    enum CompassPoint {
        case north
        case south
        case east
        case west
    }
    let directionToHead = .west
    

    不推荐

    let directionToHead = CompassPoint.west
    

    1.10 添加有必要的注释,尽可能使用Xcode注释快捷键(⌘⌥/)

    推荐

    /// <#Description#>
    ///
    /// - Parameter testString: <#testString description#>
    /// - Returns: <#return value description#>
    func testFunction(testString: String?) -> String? {
        /* ... */
    }
    

    不推荐

    // Comment
    func testFunction(testString: String?) -> String? {
        /* ... */
    }
    

    1.11 注释符//后加空格,如果//跟在代码后面,前面也加一个空格

    推荐

    // 注释
    let aString = "xxx" // 注释
    
    

    1.12 使用// MARK: -,按功能和协议/代理分组

    /// MARK顺序没有强制要求,但System API & Public API一般分别放在第一块和第二块。
    
    // MARK: - Public
    
    // MARK: - Request
    
    // MARK: - Action
    
    // MARK: - Private
    
    // MARK: - xxxDelegate
    

    1.13 对外接口不兼容时,使用@available(iOS x.0, *)标明接口适配起始系统版本号

    推荐

    @available(iOS x.0, *)
    class myClass {
        
    }
    
    @available(iOS x.0, *)
    func myFunction() {
        
    }
    

    2. 命名规范

    2.1 建议不要使用前缀

    推荐

    HomeViewController
    Bundle
    

    不推荐

    NEHomeViewController
    NSBundle
    

    2.2 不要缩写、简写、单个字母来命名

    推荐

    let viewFrame = view.frame
    

    不推荐

    let r = view.frame
    

    2.3 常量命名:以小写字母k 开头

    2.4 变量命名

    2.4.1 使用小驼峰,首字母小写
    2.4.2 变量命名应该能推断出该变量类型,如果不能推断,则需要以变量类型结尾

    推荐

    class TestClass: class {
        // UIKit的子类,后缀最好加上类型信息
        let coverImageView: UIImageView
        @IBOutlet weak var usernameTextField: UITextField!
    
        // 作为属性名的firstName,明显是字符串类型,所以不用在命名里不用包含String
        let firstName: String
    
        // UIViewContrller以ViewController结尾
        let fromViewController: UIViewController
    }
    

    不推荐

    class TestClass: class {
        // image不是UIImageView类型
        let coverImage: UIImageView
        // or cover不能表明其是UIImageView类型
        var cover: UIImageView
    
        // String后缀多余
        let firstNameString: String
    
        // UIViewContrller不要缩写
        let fromVC: UIViewController
    }
    

    2.5 类型命名:使用大驼峰表示法,首字母大写

    2.6 方法命名:使用参数标签让方法语义更清楚, 参数标签和参数需要表达正确的语义(Public接口及基础组件必须遵循)

    参数标签规则(from Swift API Design Guidelines)

    2.6.1 省略所有的冗余的参数标签

    推荐

    func min(_ number1: int, _ number2: int) {
        /* ... */
    }
    
    min(1, 2)
    
    2.6.2 进行安全值类型转换的构造方法可以省略参数标签,非安全类型转换则需要添加参数标签以表示类型转换方法

    推荐

    extension UInt32 {
      /// 安全值类型转换,16位转32位,可省略参数标签
      init(_ value: Int16)
      
      /// 非安全类型转换,64位转32位,不可省略参数标签
      /// 截断显示
      init(truncating source: UInt64)
      
      /// 非安全类型转换,64位转32位,不可省略参数标签
      /// 显示最接近的近似值
      init(saturating valueToApproximate: UInt64)
    }
    
    2.6.3 当第一个参数构成整个语句的介词时(如,at, by, for, in, to, with 等),为第一个参数添加介词参数标签

    推荐

    // 添加介词标签havingLength
    func removeBoxes(havingLength length: int) {
        /* ... */
    }
    
    x.removeBoxes(havingLength: 12)
    

    例外情况是,当后面所有参数构成独立短语时,则介词提前。

    推荐

    // 介词To提前
    a.moveTo(x: b, y: c)
    
    // 介词From提前
    a.fadeFrom(red: b, green: c, blue: d)
    

    不推荐

    a.move(toX: b, y: c)
    
    a.fade(fromRed: b, green: c, blue: d)
    
    2.6.4 当第一个参数构成整个语句一部分时,省略第一个参数标签,否则需要添加第一个参数标签

    推荐

    // 参数构成语句一部分,省略第一个参数标签
    x.addSubview(y)
    
    // 参数不构成语句一部分,不省略第一个参数标签
    view.dismiss(animated: false)
    
    2.6.5 其余情况下,给除第一个参数外的参数都添加标签

    2.7 方法命名:不要使用冗余的单词,特别是与参数及参数标签重复

    推荐

    func remove(_ member: Element) -> Element?
    
    

    不推荐

    func removeElement(_ member: Element) -> Element?
    

    2.8 命名出现缩写词,缩写词要么全部大写,要么全部小写,以首字母大小写为准

    推荐

    let urlRouterString = "https://xxxxx"
    
    let htmlString = "xxxx"
    
    class HTMLModel {
        /* ... */
    }
    
    struct URLRouter {
        /* ... */
    }
    

    不推荐

    let uRLRouterString = "https://xxxxx"
    
    let hTMLString = "xxxx"
    
    class HtmlModel {
        /* ... */
    }
    
    struct UrlRouter {
        /* ... */
    }
    

    2.9 Bool类型命名:用is最为前缀

    推荐

    var isString: Bool = true
    

    2.10 枚举定义尽量简写,不要包括类型前缀

    推荐

    public enum UITableViewRowAnimation : Int {
        case fade
    
        case right // slide in from right (or out to right)
    
        case left
    
        case top
    
        case bottom
    
        case none // available in iOS 3.0
    
        case middle // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy
    
        case automatic // available in iOS 5.0.  chooses an appropriate animation style for you
    }
    

    2.11 协议命名

    协议规则(from Swift API Design Guidelines)

    2.11.1 如果协议描述的是协议做的事应该命名为名词(eg. Collection)

    推荐

    protocol TableViewSectionProvider {
        func rowHeight(at row: Int) -> CGFloat
        var numberOfRows: Int { get }
        /* ... */
    }
    
    2.11.2 如果协议描述的是能力,需添加后缀able或 ing (eg. Equatable、 ProgressReporting)

    推荐

    protocol Loggable {
        func logCurrentState()
        /* ... */
    }
    
    protocol Equatable {
        
        func ==(lhs: Self, rhs: Self) -> bool {
            /* ... */
        }
    }
    
    2.11.3 如果已经定义类,需要给类定义相关协议,则添加Protocol后缀

    推荐

    protocol InputTextViewProtocol {
        func sendTrackingEvent()
        func inputText() -> String
        /* ... */
    }
    

    3. 语法规范

    3.1 多使用let,少使用var

    3.2 少用!去强制解包

    3.3 可选类型拆包取值时,使用if let判断

    推荐

    if let optionalValue = optionalValue {
        /* ... */
    }
    

    不推荐

    if  optionalValue != nil {
        let value = optionalValue!
        /* ... */
    }
    

    3.4 多个可选类型拆包取值时,将多个if let合并

    推荐

    var subview: UIView?
    var volume: Double?
    
    if let subview = subview, let volume = volume {
        /* ... */
    }
    

    不推荐

    var optionalSubview: UIView?
    var volume: Double?
    
    if let unwrappedSubview = optionalSubview {
      if let realVolume = volume {
        /* ... */
      }
    }
    

    3.5 不要使用 as! 或 try!

    推荐

    // 使用if let as?判断
    if let text = text as? String {
        /* ... */
    }
    
    // 使用if let try 或者 try?
    if let test = try aTryFuncton() {
        /* ... */
    }
    

    3.6 数组和字典变量定义,定义时需要标明泛型类型,并使用更简洁的语法

    推荐

    var names: [String] = []
    var lookup: [String: Int] = [:]
    

    不推荐

    var names = [String]()
    var names: Array<String> = [String]() / 不够简洁
    
    var lookup = [String: Int]()
    var lookup: Dictionary<String, Int> = [String: Int]() // 不够简洁
    
    

    3.7 数组访问尽可能使用 .first 或 .last, 推荐使用 for item in items 而不是 for i in 0..

    3.8 如果变量能够推断出类型,则不建议声明变量时指明类型

    推荐

    let value = 1 
    
    let text = "xxxx"
    

    不推荐

    let value: int = 1 
    
    let text: String = "xxxx"
    

    3.9 判等

    3.9.1 使用==!=判断内容上是否一致

    推荐

    // String类型没有-isEqualToString方法,用==判断是否相等
    let str1 = "netease"
    let str2 = "netease"
    if str1 == str2 {
        // is true
        /* ... */
    }
    
    // 对于自定义类型,判断内容是否一致,需要实现Equatable接口
    class BookItem {
        let bookId: String
        let title: String
        
        init (bookdId: String, title: String) {
            self.bookId = bookId
            self.title = title
        }
    }
    
    extension BookItem: Equatable {
    }
    
    func ==(lhs: bookItem, rhs: book: BookItem) -> bool {
        return lhs.bookId == rhs.bookId
    }
    
    3.9.2 使用===!==判断class类型对象是否同一个引用,而不是用 ==!=

    推荐

    if tenEighty === alsoTenEighty {
        /* ... */
    }
    
    if tenEighty !== notTenEighty {
        /* ... */
    }
    

    不推荐

    /// 错误写法!
    if tenEighty == alsoTenEighty {
        /* ... */
    }
    
    /// 错误写法!
    if tenEighty != notTenEighty {
        /* ... */
    }
    

    3.10 常量定义,建议尽可能定义在Type类型里面,避免污染全局命名空间

    推荐

    class TestTabelViewCell: UITableViewCell {
        static let kCellHeight = 80.0
        
        /* ... */
    }
    
    // uses
    let cellHeight = TestTabelViewCell.kCellHeight
    

    不推荐

    let kCellHeight = 80.0
    
    class TestTabelViewCell: UITableViewCell {
        /* ... */
    }
    
    // uses
    let cellHeight = kCellHeight
    

    3.11 协议

    3.11.1 当实现protocol时,如果确定protocol的实现不会被重写,建议用extension将protocol实现分离

    推荐

    class MyViewController: UIViewController {
      // class stuff here
    }
    
    // MARK: - UITableViewDataSource
    
    extension MyViewController: UITableViewDataSource {
      // table view data source methods
    }
    
    // MARK: - UIScrollViewDelegate
    
    extension MyViewController: UIScrollViewDelegate {
      // scroll view delegate methods
    }
    

    不推荐

    class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
      // all methods
    }
    
    3.11.2 当实现protocol时,如果确定protocol的实现会被重写,则可将protocol的实现放在一起
    3.11.3 当实现protocol时,建议用extension将protocol实现分离

    3.12 当方法最后一个参数是Closure类型,调用时建议使用尾随闭包语法

    推荐

    UIView.animate(withDuration: 1.0) {
      self.myView.alpha = 0
    }
    

    不推荐

    UIView.animate(withDuration: 1.0, animations: {
      self.myView.alpha = 0
    })
    

    3.13 switch case选项不需要使用break关键词

    推荐

    let someCharacter: Character = "z"
    switch someCharacter {
    case "a":
        print("The first letter of the alphabet")
    case "z":
        print("The last letter of the alphabet")
    default:
        print("Some other character")
    }
    // Prints "The last letter of the alphabet"
    

    3.14 访问控制

    3.14.1 对于私有访问,如果在文件内不能被修改,则标记为private;如果在文件内可修改,则标记为fileprivate

    推荐

    class User {
        fileprivate var name = "private"
    }
    
    extension User {
        var accessPrivate: String {
            // 同一文件内,可访问fileprivate类型属性,但不可访问private类型属性
            return name
        }
    }
    

    3.14.2 对于公有访问,如果不希望在外面继承或者override,则标记为public,否则标明为open

    推荐

    // User不可被外部继承
    public class User {
        private var name = "private"
    }
    
    // User可被外部继承
    open class User {
        private var name = "private"
    }
    
    3.14.3 访问控制权限关键字应该写在最前面,除了@IBOutletIBAction@discardableResultstatic 等关键字在最前面

    3.15 如调用者可以不使用方法的返回值,则需要使用@discardableResult标明

    推荐

    @discardableResult
    func printMessage(message: String) -> String {
        let outputMessage = "Output : \(message)"
        print(outputMessage)
    
        return outputMessage
    }
    

    3.16 Golden Path,最短路径原则

    推荐

    func login(with username: String?, password: String?) throws -> LoginError {
      guard let username = contextusername else { 
        throw .noUsername 
      }
      guard let password = password else { 
        throw .noPassword
      }
    
      /* login code */
    }
    
    

    不推荐

    func login(with username: String?, password: String?) throws -> LoginError {
      if let username = username {
        if let password = inputDatapassword {
            /* login code */
        } else {
            throw .noPassword
        }
      } else {
          throw .noUsername
      }
    }
    

    3.17 循环引用

    3.17.1 使用委托和协议时,避免循环引用,定义属性的时候使用weak修饰

    推荐

    public weak var dataSource: UITableViewDataSource?
    
    public weak var delegate: UITableViewDelegate?
    
    3.17.2 在Closures中使用self时避免循环引用

    推荐

    resource.request().onComplete { [weak self] response in
        guard let strongSelf = self else { 
            return 
        }
        let model = strongSelf.updateModel(response)
        strongSelf.updateUI(model)
    }
    

    不推荐

    // 不推荐使用unowned
    // might crash if self is released before response returns
    resource.request().onComplete { [unowned self] response in
        let model = self.updateModel(response)
        self.updateUI(model)
    }
    

    不推荐

    // deallocate could happen between updating the model and updating UI
    resource.request().onComplete { [weak self] response in
        let model = self?.updateModel(response)
        self?.updateUI(model)
    }
    

    3.18 单例

    推荐

    class TestManager {
        static let shared = TestManager()
    
        /* ... */
    }
    

    4. Objective-C兼容

    4.1 接口兼容

    4.1.1 Swift接口不对Objective-C兼容,在编译器或者Coding中就会出现错误
    4.1.2 暴漏给Objective-C的任何接口,需要添加@objc关键字,如果定义的类继承自NSObject则不需要添加
    4.1.3 如果方法参数或者返回值为空,则需要标明为可选类型

    推荐

    @objc SwiftClass {
        /* ... */
    }
    
    @objc protocol SwiftProtocol {
        /* ... */
    }
    

    4.2 Runtime兼容

    Swift语言本身对Runtime并不支持,需要在属性或者方法前添加dynamic修饰符才能获取动态型,继承自NSObject的类其继承的父类的方法也具有动态型,子类的属性和方法也需要加dynamic才能获取动态性


    参考

    相关文章

      网友评论

      本文标题:Swift编码规范

      本文链接:https://www.haomeiwen.com/subject/zpgelttx.html