Swift 代码规范

Swift,苹果于2014年WWDC苹果开发者大会发布的新开发语言,可与Objective-C共同运行于macOS和iOS平台,用于搭建基于苹果平台的应用程序。
Swift是一款易学易用的编程语言,而且它还是第一套具有与脚本语言同样的表现力和趣味性的系统编程语言。Swift的设计以安全为出发点,以避免各种常见的编程错误类别。
2015年12月4日,苹果公司宣布其Swift编程语言开放源代码。长600多页的The Swift Programming Language 可以在线免费下载。

一. 命名

  1. 类型
  • 类型名称(如 struct, enum, class, typedef, associatedtype, protocol 等)使用大驼峰命名法命名。
  • 变量和常量则以小驼峰命名法命名。
enum GenderType {
    
    case male
    case female
}

struct Person {
    
    var gender: GenderType?
}

  1. 协议
  • 根据苹果接口设计指导准则,协议名称用来描述一些东西是什么的时候是名词,例如:Collection, WidgetFactory。若协议名称用来描述能力应该以 -ing, -able, 或 -ible 结尾,例如:Equatable, Resizing

  1. 类前缀
  • Swift中类别(类,结构体)在编译时会把模块设置为默认的命名空间,所以不用为了区分类别而添加前缀,比如RW。
  • 如果担心来自不同模块的两个名称发生冲突,可以在使用时添加模块名称来区分。注意不要滥用模块名称,仅在有可能发生冲突或疑惑的场景下使用。如:
import ToolsModule 
let myClass = ToolsModule.UsefulClass()

  1. 参数命名
  • Delegate
    在定义代理方法时,第一个未命名参数应是代理数据源。

正确代码:

func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

错误代码:

func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
  • 泛型
    泛型类参数应具有描述性,遵守“大骆驼命名法”。如果一个参数名没有具体的含义,可以使用传统单大写字符,如T, U, 或V等。

正确代码:

struct Stack<Element>{ ... }
func writeTo <Target: OutputStream>(to Target: inout Target)
func swap(_ a: inout T, _ b: inout T) 

错误代码:

struct Stack<T>{ ... }
func write<target: OutputStream> (to target: inout target)
func swap<Thing>(_ a: inout Thing, _ b: inout Thing)

二. 代码逻辑

  1. 类型推断
  • 尽量使用类型推断以减少多余的冗余类型信息,例如:

正确代码:

let currentLocation = Location()

错误代码:

let currentLocation: Location = Location()

  1. self推断
  • 让编译器在所有允许的地方推断 self
  • 在 init 中设置参数时显性地使用 self
  • 在 non-escaping closures 中显性地使用 self
  • 在避免命名冲突时使用 self
    例如:
class BoardLocation  {
    let row: Int
    let column: Int
    init(row: Int, column: Int)  {
        self.row = row
        self.column = column
        let closure = {
            print(self.row)    
         }  
    }
}

  1. 常量
  • 类常量应该在类型里声明为static。使用static修饰常量可以允许他们在被引用的时候不需要实例化类型。
  • 除了单例以外,应尽量避免生成全局常量。
  • 定义常量使用 let 关键字,定义变量使用 var 关键字, 如果变量的值未来不会发生变化要使用常量。
struct PhysicsModel {
    static var speedOfLightInAVacuum = 300
}
 
class Spaceship {
    static let topSpeed = PhysicsModel.speedOfLightInAVacuum
    var speed: Double?
    func fullSpeedAhead() {
        speed = Spaceship.topSpeed
    }
}

  1. 计算型类型属性
  • 当只需要继承 getter 方法时,返回简单的 Computed 属性即可。

正确代码:

class Example {
    var age: UInt32 {
        return arc4random()
    }
}

错误代码:

class Example {
    var age: UInt32 {
        get {
            return arc4random()
        }
    }
}
  • 如果你在属性中添加了 set 或者 didSet ,那么你应该显示地提供 get 方法。
class Person {
    var age: Int {
        get {
            return Int(arc4random())
        }
        set {
            print("That's not your age.")
        }
    }
}

  1. 单例
  • Swift中单例很简单,Swift 的 runtime 会保证单例的创建并且采用线程安全的方式访问:
class ControversyManager {
    static let shared = ControversyManager()
}
  • 单例通常只需要访问"shared"的静态属性,除非你有不得已的原因去重命名它。注意,不要用静态函数或者全局函数去访问你的单例。

  1. 错误处理
  • 可以使用do/try/catch机制,避免使用try!和try?

  1. 可选值
  • 声明一个函数的某个参数可以为 nil 时,用?
  • 当你确定某个变量在使用时已经确定不是 nil 时,在后面加!

  1. 扩展声明周期
  • 用 [weak self] 和 guard let strongSelf = self else { return } 模式扩展生命周期。用 [weak self] 比 [unowned self] 更好。

正确代码:

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

错误代码:

// 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)
}

三. 代码结构

  1. 注释
  • 使用Xcode8自带的注释功能,快捷键Option+Command+/
  • 使用// MARK:分隔代码(类似于OC中的#pragma mark)

  1. 缩进
  • 遵守Xcode内置的缩进格式

  1. 类型
  • 优先使用Swift原生类型,可以根据需要使用Objective-C提供的方法,因为Swift提供了到Objective-C的桥接 。

正确代码:

let width = 120.0  // Double
let widthString = (width as NSNumber).stringValue // String

错误代码:

let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue  // NSString

  1. 协议一致性
  • 当一个对象要实现协议一致性时,推荐使用 extension 隔离协议中的方法集,这样让相关方法和协议集中显示在一起,也简化了类支持一个协议和实现相关方法的流程。

正确代码:

class ViewController: UIViewController {
      // 方法
} 

extension ViewController: UITableViewDataSource {
      // UITableViewDataSource 方法
}

extension ViewController: UIScrollViewDelegate {
      // UIScrollViewDelegate 方法
} 

错误代码:

class ViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
      // 所有方法
} 

  1. 良好的定义
  • 属性,变量,常量和参数等在声明定义时,其中: 符号后有空格,而: 符号前没有空格,比如x: Int, 和Circle: Shape
  • 定义多个变量/数据结构时,出于相同的目的和上下文,可以定义在同一行。
  • 缩进 getter,setter 的定义和属性观察器的定义。
  • 不需要添加internal这样的默认的修饰符。也不需要在重写一个方法时添加访问修饰符。
  • 给那些不打算被继承的类使用final修饰符
final class Circle: Shape {
  var x: Int
  var y: Int
  var radius: Double
  var diameter: Double {
      get {
        returnradius * 2
     } 
     set {      
        radius=newValue/2
    }  
  }
  init(x: Int, y: Int, radius: Double) {
      self.x = x
      self.y = y
      self.radius = radius  
  }

  convenience init(x: Int, y: Int, diameter: Double) {
     self.init(x: x, y: y, radius: diameter/2)  
  }

  func describe() -> String{
    return"I am a circle at\(centerString())with an area of\(computeArea())"
  }
  override func computeArea() -> Double{
    return M_PI * radius * radius  
  }

  private func centerString()->String{
    return "(\(x),\(y))"
  }
}

  1. 控制流程
  • 循环使用for-in表达式,而不使用 while 表达式。

正确代码:

for _ in 0..<3 {
  print("Hello three times")
}
for(index, person) in attendeeList.enumerate() {
    print("\(person)is at position #\(index)")
}
for index in 0.stride(from: 0, to: items.count, by: 2) {
  print(index)
}
for index in (0...3).reverse() {
    print(index)
}

错误代码:

var i=0 
while i<3 {
  print("Hello three times") 
  i+=1
}
var i=0
while i<3 {
  let person = attendeeList[i]
  print("\(person)is at position #\(i)")  
  i+=1
}
  • Switch 模块中不用显式使用break。

  1. 黄金路径
  • 当编码遇到条件判断时,左边的距离是黄金路径或幸福路径,因为路径越短,速度越快。不要嵌套if循环,多个返回语句是可以的。guard 就为此而生的。

正确代码:

func computeFFT(context: Context?, inputData: InputData?)  throws -> Frequencies {
    guard let context = context else {throwFFTError.NoContext }

   guard let inputData = inputData else { throwFFTError.NoInputData }

  //计算frequencies
  return frequencies
}

错误代码:

func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
if let context = context {
    if let inputData = inputData {
           // 计算frequencies
          return frequencies    
    } else {
         throwFFTError.NoInputData    
     }  
   } else {
      throwFFTError.NoContext  
   }
}
  • 当有多个条件需要用 guard 或 if let 解包,可用复合语句避免嵌套。
guard let number1=number1, number2=number2, number3=number3 else{
    fatalError("impossible") 
}
// 处理number

  1. 语法糖
//对空的数据和字典,使用类型注解
var names:  [String] = []
var lookup:  [String: Int] = [:]

var deviceModels: [String]
var employees: [Int : String]
var faxNumber: Int?

  1. 函数
  • 自由函数不依附于任何类或类型,应该节制地使用。

正确代码:

let sorted = items.mergeSort() // 易发现性
rocket.launch()  // 可读性

错误代码:

let sorted = mergeSort(items)// 不易被发现
launch(&rocket)

天然的自由函数:

let value = max(x,y,z)  // another free function that feels natural

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容