【Swift编码规范】(下篇)

阿凡达2018-08-21 11:17

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.163yun.com/gift

本文来自网易实践者社区,经作者胡波授权发布。