Swift編程規(guī)范

1. 目錄

[TOC]

2. 修訂信息

版本 時間 修訂人 修訂內(nèi)容
1.0 2016-03-28 麥志泉 創(chuàng)建文檔

3. 概述

為幫助開發(fā)者實現(xiàn)代碼的優(yōu)美與一致性、可讀性和簡潔性逃贝,
本人編寫了Swift編程規(guī)范指南,包含命名盟迟、空格秋泳、注釋、類和結(jié)構(gòu)體攒菠、
函數(shù)聲明迫皱、閉包表達(dá)式、類型辖众、控制流卓起、分號等。


4. 規(guī)范指南

4.1 命名(Naming)

使用駝峰式的描述性命名方式凹炸,為類戏阅,方法,變量等命名啤它。類名的首字母應(yīng)該大寫奕筐,而方法和變量的首字母使用小寫字符舱痘。

推薦做法:

private 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
}

對于函數(shù)和初始化方法,推薦對所有的參數(shù)進(jìn)行有意義的命名离赫,
除非上下文已經(jīng)非常清楚芭逝。如果外部參數(shù)命名可以使得函數(shù)調(diào)用更加可讀,
也應(yīng)該把外部參數(shù)命名包含在內(nèi)渊胸。

func dateFromString(dateString: String) -> NSDate
func convertPointAt(#column: Int, #row: Int) -> CGPoint
func timedAction(#delay: NSTimeInterval, perform action: SKAction) -> SKAction!
// 調(diào)用方式如下:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(delay: 1.0, perform: someOtherAction)

對于方法來說旬盯,參照標(biāo)準(zhǔn)的蘋果慣例,方法命名含義要引用到第一個參數(shù):

class Guideline {
  func combineWithString(incoming: String, options: Dictionary?) { ... }
  func upvoteBy(amount: Int) { ... }
}

4.2 枚舉(Enumerations)

使用首字母大寫的駝峰命名規(guī)則來命名枚舉值:

enum Shape {
  case Rectangle
  case Square
  case Triangle
  case Circle
}

4.3 文章(Prose)

當(dāng)我們在文章中(教程翎猛,圖書胖翰,注釋等)需要引用到函數(shù)時,需要從調(diào)用者的視角考慮切厘,包含必要的參數(shù)命名萨咳,或者使用_表示不需要命名的參數(shù)。

從你自身實現(xiàn)的init中調(diào)用convertPointAt(column:row:)迂卢。
如果你調(diào)用dateFromString(_:),需要保證你提供的輸入字符串格式是”yyyy-MM-dd”某弦。
如果你需要在viewDidLoad()中調(diào)用timedAction(delay:perform:),記得提供調(diào)整后的延遲值和需要處理的動作而克。

你不能直接調(diào)用數(shù)據(jù)源方法tableView(_:cellForRowAtIndexPath:)
當(dāng)你遇到疑問時,可以看看Xcode在jump bar中是如何列出方法名的 —— 我們的風(fēng)格與此匹配怔毛。

image

4.4 類的前綴(Class Prefixes)

Swift類型自動被模塊名設(shè)置了名稱空間员萍,所以你不需要加一個類的前綴。如果兩個來自不同模塊的命名沖突了拣度,你可以附加一個模塊名到類型命名的前面來消除沖突碎绎。

import SomeModule
let myClass = MyModule.UsefulClass()

4.5 空格(Spacing)

使用Xcode默認(rèn)配置的數(shù)字就是

  • 方法定義的大括號或者其他大括號(if/else/switch/while等)- 一般都放在定義名稱的同一行,
    并且使用一個新的行來結(jié)束抗果。
  • 提示:你可以通過以下方法重新進(jìn)行縮進(jìn):選擇一些代碼(或者使用?A選擇所有)筋帖,然后按Control-I(或者點擊菜單欄 Editor\Structure\Re-Indent)。一些Xcode模板代碼使用的縮進(jìn)是4個空格冤馏,所以這種方法可以很好的修復(fù)縮進(jìn)日麸。

推薦做法:

if user.isHappy {
    // Do something
} else {
    // Do something else
}

4.6 注釋(Comments)

枚舉注釋:

/**
 證件印

 - MainLand:  中國大陸
 - HKorMacao: 香港澳門地區(qū)
 - Taiwan:    臺灣
 - Oversea:   海外
 */
enum IdcardAreaType: String {
    case MainLand = "1"
    case HKorMacao = "2"
    case Taiwan = "3"
    case Oversea = "4"
}

方法注釋:

/**
 向下取第幾位小數(shù)

 - parameter places: 第幾位小數(shù) ,1

 15.96 * 10.0 = 159.6
 floor(159.6) = 159.0
 159.0 / 10.0 = 15.9

 - returns:  15.96 =  15.9
 */
func f(places:Int) -> Double {
    let divisor = pow(10.0, Double(places))
    return floor(self * divisor) / divisor
}
}

4.7 定義的案例(Example definition)

以下是一個風(fēng)格很好的類定義:

class Circle: Shape {  
  var x: Int, y: Int  
  var radius: Double  
  var diameter: Double {  
    get {  
      return radius * 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))"  
  }  
}  

以上例子遵循了以下風(fēng)格規(guī)范:

  • 指定屬性逮光、變量代箭、常量、參數(shù)定義或者其他定義的類型涕刚,在冒號后面嗡综,緊跟著一個空格,而不是把空格放在冒號前面杜漠。比如:x: Int和Circle: Shape极景。
  • 如果能表示相同的目的和上下文察净,可以在同一行定義多個變量和結(jié)構(gòu)體。
  • 縮進(jìn)getter盼樟,setter的定義和屬性觀察器的定義塞绿。
  • 不需要添加internal這樣的默認(rèn)的修飾符。同樣的恤批,不需要在重寫一個方法時添加訪問修飾符异吻。

4.8 Self的使用(Use of Self)

為了保持簡潔,避免使用 self 關(guān)鍵詞喜庞,Swift 不需要使用 self 來訪問對象屬性和調(diào)用對象方法诀浪。

必須使用 self 來區(qū)分構(gòu)造器中屬性命名和參數(shù)命名,還有在閉包表達(dá)式中引用屬性值(編譯器需要區(qū)分):

class BoardLocation {  
  let row: Int, column: Int  
  init(row: Int, column: Int) {  
    self.row = row  
    self.column = column  
    let closure = {  
      println(self.row)  
    }  
  }  
}

4.9 協(xié)議遵守(Protocol Conformance)

當(dāng)我們對一個類添加協(xié)議時延都,推薦使用一個單獨的類擴展來實現(xiàn)協(xié)議的方法雷猪。這可以保持協(xié)議相關(guān)的方法聚合在一起,同時也可以簡單的標(biāo)識出一個協(xié)議對應(yīng)類中需要實現(xiàn)哪些對應(yīng)的方法晰房。

同時求摇,別忘了添加// MARK:,注釋可以使得代碼組織的更好殊者!

推薦做法:

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  
}

4.9 計算屬性(Computed Properties)

為了保持簡潔与境,如果一個計算屬性是只讀的,請忽略掉get語句猖吴。只有在需要定義set語句的時候摔刁,才提供get語句。

推薦做法:

var diameter: Double {  
  return radius * 2  
}  

不推薦做法:

var diameter: Double {  
  get {  
    return radius * 2  
  }  
}

4.10 函數(shù)聲明(Function Declarations)

保證短的函數(shù)定義在同一行中海蔽,并且包含左大括號:

func reticulateSplines(spline: [Double]) -> Bool {  
  // reticulate code goes here  
}  

在一個長的函數(shù)定義時共屈,在適當(dāng)?shù)牡胤竭M(jìn)行換行,同時在下一行中添加一個額外的縮進(jìn):

func reticulateSplines(spline: [Double], adjustmentFactor: Double,  
    translateConstant: Int, comment: String) -> Bool {  
  // reticulate code goes here  
}  

4.11 閉包表達(dá)式(Closure Expressions)

如果閉包表達(dá)式參數(shù)在參數(shù)列表中的最后一個時党窜,使用尾部閉包表達(dá)式拗引。給定閉包參數(shù)一個描述性的命名。

推薦做法:

UIView.animateWithDuration(1.0) {  
  self.myView.alpha = 0  
}  
UIView.animateWithDuration(1.0,  
  animations: {  
    self.myView.alpha = 0  
  },  
  completion: { finished in  
    self.myView.removeFromSuperview()  
  }  
)  

不推薦做法:

UIView.animateWithDuration(1.0, animations: {  
  self.myView.alpha = 0  
})  
UIView.animateWithDuration(1.0,  
  animations: {  
    self.myView.alpha = 0  
  }) { f in  
    self.myView.removeFromSuperview()  
}  

當(dāng)單個閉包表達(dá)式上下文清晰時幌衣,使用隱式的返回值:

attendeeList.sort { a, b in  
  a > b  
}  

4.12 類型(Types)

盡可能使用 Swift 原生類型矾削。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

在Sprite Kit代碼中怔软,使用CGFloat可以使得代碼更加簡明,避免很多轉(zhuǎn)換择镇。

4.13 常量(Constants)

常量定義使用 let 關(guān)鍵字挡逼,變量定義使用 var 關(guān)鍵字,如果變量的值不需要改變腻豌,請盡量使用 let 關(guān)鍵字家坎。

提示:一個好的技巧是嘱能,使用 let 定義任何東西,只有在編譯器告訴我們值需要改變的時候才改成 var 定義虱疏。

全局或類的成員常量命名前加k

//MARK: - 常量
let kMarketChartDataRefreshTime: NSTimeInterval = 60      //數(shù)據(jù)刷新間隔
let kMarketDataRefreshTime: NSTimeInterval = 4      //數(shù)據(jù)刷新間隔

4.14 可選類型(Optionals)

當(dāng)nil值是可以接受的時候時惹骂,定義變量和函數(shù)返回值為可選類型(?)。

當(dāng)你確認(rèn)變量在使用前已經(jīng)被初始化時做瞪,使用!來顯式的拆包類型对粪,比如在viewDidLoad中會初始化subviews。

當(dāng)你訪問一個可選值時装蓬,如果只需要訪問一次或者在可選值鏈中有多個可選值時著拭,請使用可選值鏈:

self.textContainer?.textLabel?.setNeedsDisplay()  

當(dāng)需要很方便的一次性拆包或者添加附加的操作時,請使用可選值綁定:

if let textContainer = self.textContainer {  
  // do many things with textContainer  
}  

當(dāng)我們命名一個可選變量和屬性時牍帚,避免使用諸如optionalString和maybeView這樣的命名儡遮,因為可選值的表達(dá)已經(jīng)在類型定義中了。

在可選值綁定中暗赶,直接映射原始的命名比使用諸如unwrappedView和actualLabel要好鄙币。

推薦做法:

var subview: UIView?  
var volume: Double?  
// later on...  
if let subview = subview, volume = volume {  
  // do something with unwrapped subview and volume  
}  

不推薦做法:

var optionalSubview: UIView?  
var volume: Double?  
if let unwrappedSubview = optionalSubview {  
  if let realVolume = volume {  
    // do something with unwrappedSubview and realVolume  
  }  
}

4.15 結(jié)構(gòu)體構(gòu)造器(Struct Initializers)

使用原生的 Swift 結(jié)構(gòu)體構(gòu)造器,比老式的幾何類(CGGeometry)的構(gòu)造器要好蹂随。

推薦做法:

let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)  
let centerPoint = CGPoint(x: 96, y: 42)  

不推薦做法:

let bounds = CGRectMake(40, 20, 120, 80)  
let centerPoint = CGPointMake(96, 42)  

推薦使用結(jié)構(gòu)體限定的常量CGRect.infiniteRect,CGRect.nullRect等十嘿,來替代全局常量CGRectInfinite,CGRectNull等。對于已經(jīng)存在的變量糙及,可以直接簡寫成 .zeroRect详幽。

4.15 類型推斷(Type Inference)

推薦使用更加緊湊的代碼,讓編譯器能夠推斷出常量和變量的類型浸锨。除非你需要定義一個特定的類型(比如CGFloat和Int16),而不是默認(rèn)的類型版姑。

推薦做法:

let message = "Click the button"  
let currentBounds = computeViewBounds()  
var names = [String]()  
let maximumWidth: CGFloat = 106.5  

不推薦做法:

let message: String = "Click the button"  
let currentBounds: CGRect = computeViewBounds()  
var names: [String] = []  

注意: 遵守這條規(guī)則意味選擇描述性命名比之前變得更加重要柱搜。

4.16 語法糖(Syntactic Sugar)

推薦使用類型定義簡潔的版本,而不是全稱通用語法剥险。

推薦做法:

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

不推薦做法:

var deviceModels: Array<String>  
var employees: Dictionary<Int, String>  
var faxNumber: Optional<Int>  

4.17 控制流(Control Flow)

推薦循環(huán)使用for-in表達(dá)式聪蘸,而不使用for-condition-increment表達(dá)式。

推薦做法:

for _ in 0..<3 {  
  println("Hello three times")  
}  
for (index, person) in enumerate(attendeeList) {  
  println("\(person) is at position #\(index)")  
}  

不推薦做法:

for var i = 0; i < 3; i++ {  
  println("Hello three times")  
}  
for var i = 0; i < attendeeList.count; i++ {  
  let person = attendeeList[i]  
  println("\(person) is at position #\(i)")  
}  

4.17 ?類成員變量聲明

蘋果UIKit框架中的UIButton, UILabel等組件類表制,聲明最好以組件名開頭健爬,這樣在代碼提示中容易被檢索歸類

推薦做法:

/// MARK: - 成員變量
@IBOutlet var tableViewWealth: UITableView!
@IBOutlet var labelTotalAssetsTitle: UILabel!
@IBOutlet var labelTotalAssets: UILabel!

當(dāng)類使用到代理協(xié)議的設(shè)計模式時,delegate前要加weak

weak var delegate: BankListViewDelegate?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末么介,一起剝皮案震驚了整個濱河市娜遵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壤短,老刑警劉巖设拟,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慨仿,死亡現(xiàn)場離奇詭異,居然都是意外死亡纳胧,警方通過查閱死者的電腦和手機镰吆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跑慕,“玉大人万皿,你說我怎么就攤上這事『诵校” “怎么了牢硅?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钮科。 經(jīng)常有香客問我唤衫,道長,這世上最難降的妖魔是什么绵脯? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任佳励,我火速辦了婚禮,結(jié)果婚禮上蛆挫,老公的妹妹穿的比我還像新娘赃承。我一直安慰自己,他們只是感情好悴侵,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布瞧剖。 她就那樣靜靜地躺著,像睡著了一般可免。 火紅的嫁衣襯著肌膚如雪抓于。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天浇借,我揣著相機與錄音捉撮,去河邊找鬼。 笑死妇垢,一個胖子當(dāng)著我的面吹牛巾遭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闯估,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼灼舍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涨薪?” 一聲冷哼從身側(cè)響起骑素,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尤辱,沒想到半個月后砂豌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厢岂,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年阳距,在試婚紗的時候發(fā)現(xiàn)自己被綠了塔粒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡筐摘,死狀恐怖卒茬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咖熟,我是刑警寧澤圃酵,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站馍管,受9級特大地震影響郭赐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜确沸,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一捌锭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罗捎,春花似錦观谦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倒得,卻和暖如春泻红,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霞掺。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工承桥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人根悼。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像蜀撑,于是被迫代替她去往敵國和親挤巡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理酷麦,服務(wù)發(fā)現(xiàn)矿卑,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,776評論 1 10
  • 86.復(fù)合 Cases 共享相同代碼塊的多個switch 分支 分支可以合并, 寫在分支后用逗號分開错英。如果任何模式...
    無灃閱讀 1,345評論 1 5
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評論 6 342
  • 1暈輪效應(yīng) 在愛情的世界里织狐,最美好的就是我喜歡你而你也喜歡著我。作為一名單身的男性看到漂亮的姑娘總是...
    杜小才閱讀 842評論 0 0