Swift编码风格指南

swift

最近在阅读swift进阶这本书,其中的风格指南,我每次打开书要读的时候,我都会先看一遍这个指南,因为觉得真的很好.这里贴在简书上,和大家分享一下,有些指南后面我会加上示例代码:

对于命名,在使用时能清晰表意是最重要。因为 API 被使用的次数要远远多于被声明的次数,所以我们应当从使用者的角度来考虑它们的名字。尽快熟悉 Swift API 设计准则,并且在你自己的代码中坚持使用这些准则

// 尽量让命名语义化,而且swift不需要前缀,因为有命名空间

// GOOD
extension List {
  public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)

// 反例

// BAD
extension List {
  public mutating func removeAt(position: Index) -> Element
}
employees.remove(x)

简洁经常有助于代码清晰,但是简洁本身不应该独自成为我们编码的目标

务必为函数添加文档注释 — 特别是泛型函数

    // 官方的API里面map函数的注释文档,记住如果你想让你的注释
    // 像官方文档一样按住option单击左键就能看到,请使用 /// 
    
    // GOOD
    /// Returns an array containing the results of mapping the given closure
    /// over the sequence's elements.
    ///
    /// In this example, `map` is used first to convert the names in the array
    /// to lowercase strings and then to count their characters.
    ///
    ///     let cast = ["Vivien", "Marlon", "Kim", "Karl"]
    ///     let lowercaseNames = cast.map { $0.lowercaseString }
    ///     // 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"]
    ///     let letterCounts = cast.map { $0.characters.count }
    ///     // 'letterCounts' == [6, 6, 3, 4]
    ///
    /// - Parameter transform: A mapping closure. `transform` accepts an
    ///   element of this sequence as its parameter and returns a transformed
    ///   value of the same or of a different type.
    /// - Returns: An array containing the transformed elements of this
    ///   sequence.
    public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
    
    // 反例,下面这种注释是很多人会范的错
    // 更值得提示的是,请多使用MARK来给业务逻辑分段
    
    // BAD
    // map函数
    public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

类型使用大写字母开头,函数、变量和枚举成员使用小写字母开头,两者都使用驼峰式命名法

// Class使用大写字母开头就不用多说了.
// 这里需要注意的是,在swift中,enum使用小写字母开头
// 这个跟OC有很大区别

// GOOD
enum RequestPath {
    case index
    case discover
}

// BAD
enum RequestPath {
    case Index
    case Discover
}

使用类型推断。省略掉显而易见的类型会有助于提高可读性

// 有人觉得使用类型推断会使得执行效率下降
// 笔者可以负责任的告诉你,完全不会,因为类型推断是编译器完成的

// GOOD
let str: String = "ABC"

// BAD
let str = "ABC"

优先选择结构体,只在确实需要使用到类特有的特性或者是引用语义时才使用类

swiftstruct是值类型(value type),而类是引用类型(reference type),值类型在传递的过程中是会被拷贝的,所以无副作用

除非你的设计就是希望某个类被继承使用,否则都应该将它们标记为 final

swift中被标记final关键字的类,都是不允许继承的,如果是方法被标记了final,则是不允许重写

除非一个闭包后面立即跟随有左括号,否则都应该使用尾随闭包 (trailing closure) 的语法

// GOOD       
UIView.animate(withDuration: 0.3) {
            
}

// BAD
UIView.animate(withDuration: 0.3, animations: {
            
})

使用 guard 来提早退出方法

// guard可以使得条件不成立的时候执行else里面的代码
// 如果成立则继续执行下面的代码

// GOOD
guard webView.isKind(of: WKWebView.self) else {
  return
}
// 执行业务代码

// BAD
if webView.isKind(of: WKWebView.self) {
  // 执行业务代码          
} else {
            
}

避免对可选值进行强制解包和隐式强制解包。它们偶尔有用,但是经常需要使用它们的话往往意味着有其他不妥的地方

这里没啥可说的,谁用谁知道,结果就是crash,所以建议使用optional chains

self.textContainer?.textLabel?.setNeedsDisplay() // 保平安

不要写重复的代码。如果你发现你写了好几次类似的代码片段的话,试着将它们提取到一个函数里,并且考虑将这个函数转化为协议扩展的可能性

试着去使用 map 和 reduce,但这不是强制的。当合适的时候,使用 for 循环也无可厚非。高阶函数的意义是让代码可读性更高。但是如果使用 reduce 的场景难以理解的话,强行使用往往事与愿违,这种时候简单的 for 循环可能会更清晰

// map
numbers.map { $0 * 2 }

// for
for i in numbers {
  i * 2
}

试着去使用不可变值:除非你需要改变某个值,否则都应该使用 let 来声明变量。不过如果能让代码更加清晰高效的话,也可以选择使用可变的版本。用函数将可变的部分封装起来,可以把它带来的副作用进行隔离

除非你确实需要,否则不要使用 self.。在闭包表达式中,使用 self 是一个清晰的信号,表明闭包将会捕获 self

// 有些开发者是从Objective - C转过来的,非常喜欢使用self
// 其实在swift中完全没有必要
// 但是在闭包中使用self是规定

anotherNum.map { num in
  self.numbers.append(num)
}

尽可能地对现有的类型和协议进行扩展,而不是写一些全局函数。这有助于提高可读性,让别人更容易发现你的代码

// 多用extension
// 在开发中经常遇到这样的问题
// 如果把代理方法直接放在当前类里面会使得代码臃肿不堪
// 通过extension可以使得两者分离开
// 如果你愿意,你甚至可以写在两个文件里面都没有关系
// 比如代理方法,完全可以通过extension的方式使用
// 这样是得代码的调理性更加清晰

extension WebViewController: WKUIDelegate, WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.title = webView.title!
    }
}

生命不息,折腾不止...
I'm not a real coder, but i love it so much!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容