該文章由 iOSCaff 社群 組織翻譯,後續社群會保持文章的更新,如果你覺得這篇文章對你有幫助,歡迎到社群點贊支援。
譯文地址:https://ioscaff.com/topics/84/raywenderlich-official-swift-style-guide
原文地址:https://github.com/raywenderlich/swift-style-guide
已更新到 Swift 3
這篇風格指南可能不同於你看到的其他風格指南。因為它的重點偏向於列印和網頁的可讀性。我們建立這篇風格指南的目的,是為了讓我們的書、教程和初學者套件中的程式碼,在有很多作者同時寫書的情況下,也能保持規範與一致。
我們的首要目標是清晰、一致和簡潔。
正確性
努力讓你的程式碼在沒有警告的情況下編譯。 這條規則決定了許多風格決策,比如使用 #selector
型別而不是字串字面量。
命名
描述性和一致性的命名讓軟體更易於閱讀和理解。使用 API 設計規範 中描述的 Swift 命名規範。 一些關鍵點包括如下:
- 儘量讓呼叫的地方更加簡明
- 簡明性優先而不是簡潔性
- 使用駝峰命名法(而不是蛇形命名法)
- 針對型別(和協議)使用首字母大寫,其它都是首字母小寫
- 包含所有需要的單詞,同時省略不必要的單詞
- 基於角色的命名,而不是型別
- 有時候要針對弱引用型別資訊進行補充
- 儘量保持流暢的用法
- 工廠方法以
make
開頭 - 命名方法的副作用
- 不可變版本的動詞方法要遵循後接 -ed, -ing 的規則
- 可變版本的名詞方法要遵循 formX 的規則
- 布林型別應該像斷言一樣讀取
- 描述 這是什麼 的協議應該讀作名詞
- 描述 一種能力 的協議應該以 -able 或者 -ible 結尾
- 使用不會讓專家驚訝或讓初學者迷惑的術語
- 通常要避免縮寫
- 使用名稱的先例
- 首選方法和屬性而不是自由函式
- 統一向上或向下包裝首字母縮略詞和首字母
- 為相同含義的方法提供相同的基本名稱
- 避免返回型別的過載
- 選擇用於文件的好的引數名
- 為閉包和元組引數設定標籤
- 利用預設引數的優勢
文章
在文章中引用方法時,含義明確是至關重要的。儘可能用最簡單的形式引用方法。
- 寫一個不帶引數的方法。 舉例: 下一步,你需要呼叫方法
addTarget
。 - 寫一個帶引數標籤的方法。 舉例: 下一步,你需要呼叫方法
addTarget(_:action:)
。 - 寫一個帶引數標籤和型別的完整方法。 舉例: 下一步, 你需要呼叫方法
addTarget(_: Any?, action: Selector?)
。
用上面的例子使用 UIGestureRecognizer
, 1 是明確的,也是首選的。
專家提示: 你可以用 Xcode 的跳轉欄來檢視帶有引數標籤的方法。
類字首
Swift 的類自動被包含在模組分配的名稱空間中。不應該再新增類似於 RW 的類字首。如果不同模組的兩個命名衝突,可以在類名前新增模組名來消除歧義。無論如何,僅在少數可能引起混淆的情況下指明模組名。
import SomeModule
let myClass = MyModule.UsefulClass()
複製程式碼
代理
當建立自定義代理方法的時候,未命名的第一個引數應該是代理源。 ( UIKit 包含很多這樣的例子。)
推薦:
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
複製程式碼
不推薦:
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
複製程式碼
使用上下文推斷的型別
使用上下文推斷編譯器書寫更短更明確的程式碼。(你也可以閱讀 型別推斷。)
推薦:
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
複製程式碼
不推薦:
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
複製程式碼
一般的
一般的型別引數應該是描述性的、大寫駝峰法命名。當類名沒有富有含義的關係或角色時,使用傳統的單個大寫字母來命名,例如 T
、 U
或 V
。
推薦:
struct Stack<Element> { ... }
func write<Target: OutputStream>(to target: inout Target)
func swap<T>(_ 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)
複製程式碼
語言
使用美式英語拼寫來匹配 Apple 的 API。
推薦:
let color = "red"
複製程式碼
不推薦:
let colour = "red"
複製程式碼
程式碼組織
用擴充套件將程式碼組織為功能邏輯塊。每個擴充套件都應該新增 // MARK: -
註釋,以保證程式碼的結構清晰。
協議遵循
推薦為協議方法加一個單獨的擴充套件,尤其是為一個模型加入協議遵循的時候。這可以讓有關聯的協議方法被分組在一起,也可以簡化用類關聯方法向這個類新增協議的指令。
推薦:
class MyViewController: UIViewController {
// 類填充在這
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view 的資料來源方法
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view 的代理方法
}
複製程式碼
不推薦:
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// 所有方法
}
複製程式碼
因為編譯器不允許在派生類中重新宣告協議遵循,所以並不總是需要複製基類的擴充套件組。如果派生類是一個終端類,並且只有少數方法會被覆蓋,那麼這個原則尤為正確。應由作者自行決定何時保留擴充套件組- 。
對於 UIKit 中的檢視控制器,可考慮將生命週期、自定義存取器和 IBAction 分組在單獨的類擴充套件中。
無用程式碼
無用程式碼(殭屍程式碼),包括 Xcode 模板程式碼和佔位註釋,應該被移除掉。教程或書籍中教使用者使用的註釋程式碼除外。
僅實現簡單呼叫父類,但與教程無直接關聯的方法應該被移除。這裡包括任何為空的或無用的 UIApplicationDelegate 方法。
推薦:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}
複製程式碼
不推薦:
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 任何可以重建資源的處理。
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning 未完成的實現,返回節數。
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning 未完成的實現,返回行數。
return Database.contacts.count
}
複製程式碼
最小引用
引用最小化。舉個例子,引用 Foundation
就足夠的情況下不要再引用 UIKit
。
空格
- 用兩個字元縮排比用製表符縮排更節省空間,同時能防止換行。務必在 Xcode 和專案中設定這個偏好,如下所示:
- 方法大括號和其他大括號(
if
/else
/switch
/while
等)總是在和語句相同的行寫左括號,而在新行寫右括號。 - 提示:你可以通過選中一些程式碼(或按 ⌘A 選中全部)然後按 Control-I (或在目錄中選擇編輯器 -> 結構 -> 重新縮排)的方式來重新縮排程式碼。一些 Xcode 模板程式碼會使用 4 個空格的製表符硬編碼,這就是一個修正它的好方法。 推薦:
if user.isHappy {
// 做一件事
} else {
// 做另一件事
}
複製程式碼
不推薦:
if user.isHappy
{
// 做一件事
}
else {
// 做另一件事
}
複製程式碼
- 方法之間應該只有一個空行,這樣有助於視覺清晰和組織。方法中的空白應該按功能分隔程式碼,但在一個方法中有很多段意味著你應該將它們封裝進不同的方法。
- 冒號總是在左邊沒有空格而右邊有空格。比較特殊的是三元運算子
? :
、空字典[:]
和帶有未命名引數(_:)
的#selector
語法 .
推薦:
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
複製程式碼
不推薦:
class TestDatabase : Database {
var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}
複製程式碼
- 長行應該在 70 個字元左右被換行(這裡並非硬性限制,可自行調整)。
- 避免在行結尾的地方附上空白。
- 在每個檔案的結尾處增加一個單獨的換行符。
註釋
需要的時候,用註釋來解釋一個特定的程式碼片段 為什麼 做某件事。註釋應保持要麼是最新的,要麼就被刪除。
為了避免塊註釋和程式碼內聯,程式碼應該儘可能自文件化。 例外:這不含那些註釋被用於生成文件的情況 。
類和結構體
使用哪個?
請記住,結構體有 值語義。對沒有標識的事物應用結構體。一個包含 [a, b, c] 的陣列和另一個包含 [a, b, c] 的陣列是完全一樣的。他們是可以完全互換的。使用第一個陣列還是第二個陣列都無所謂,因為他們代表著完全相同的事物。這就是為什麼陣列是結構體。
類有 引用語義。對有標識或有具體生命週期的事物應用類。你需要將人建模為一個類,因為不同兩個人物件是兩個不同的事物。只是因為兩個人擁有相同的名字和生日不意味著他們是同一個人。但是人的生日應該是一個結構體,因為 1950 年 3 月 3 日和任何其它的 1950 年 3 月 3 日日期物件是相同的。日期本身沒有標識。
有時,事物應該是結構體但需要遵循 AnyObject
,或在歷史上已經被建模為類 (NSDate
、 NSSet
)。儘可能嘗試遵循這些原則。
定義的舉例
這是一個風格良好的類定義例子:
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)
}
override func area() -> Double {
return Double.pi * radius * radius
}
}
extension Circle: CustomStringConvertible {
var description: String {
return "center = \(centerString) area = \(area())"
}
private var centerString: String {
return "(\(x),\(y))"
}
}
複製程式碼
上面的例子遵循了以下風格規範:
- 用後面有空格而前面沒有空格的冒號,為屬性、變數、常量、引數宣告和其它語句指定型別,例如:
x: Int
和Circle: Shape
。 - 如果多個變數和結構體共享一個共同的目的 / 上下文,則可以在同一行中定義。
- 縮排 getter、setter 的定義和屬性觀察器。
- 不要再新增如
internal
的預設修飾符。類似的,當重寫一個方法時,不要再重複新增訪問修飾符。 - 在擴充套件中組織額外功能(例如列印)。
- 隱藏非共享的實現細節,例如
centerString
在擴充套件中使用private
訪問控制。
Self 的使用
為了簡潔,請避免使用 self
關鍵詞,Swift 不需要用它來訪問一個物件屬性或呼叫它的方法。
僅在編譯器需要時(在 @escaping
閉包或初始化函式中,消除引數與屬性的歧義)才使用 self。換句話說,如果不需要 self
就能編譯通過,則可以忽略它。
計算屬性
為了簡潔,如果一個計算屬性是隻讀的,則可以忽略 get 子句。僅在提供了 set 子句的情況下才需要 get 子句。
推薦:
var diameter: Double {
return radius * 2
}
複製程式碼
不推薦:
var diameter: Double {
get {
return radius * 2
}
}
複製程式碼
Final
在教程中將類或成員標記為 final
會從主題分散注意力,而且也沒必要。 儘管如此,final
的使用有時可以表明你的意圖,且值得你這樣做。在下面的例子中,Box
有特定的目的,且並不打算在派生類中自定義它。標記為 final
可以使它更清晰。
// 用這個 Box 類將任何一般型別轉換為引用型別。
final class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
複製程式碼
方法宣告
在一行中保持較短的方法宣告,包括左括號:
func reticulateSplines(spline: [Double]) -> Bool {
// 在這裡寫網格程式碼
}
複製程式碼
對於簽名較長的函式,則需在合適的位置換行,然後在後續的行中加一個額外的換行:
func reticulateSplines(spline: [Double], adjustmentFactor: Double,
translateConstant: Int, comment: String) -> Bool {
// 在這裡寫網路程式碼
}
複製程式碼
閉包表示式
僅在引數列表最後有個單獨的閉包表示式引數時,使用尾隨閉包語法。給閉包引數定義一個描述性的命名。
推薦:
UIView.animate(withDuration: 1.0) {
self.myView.alpha = 0
}
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}, completion: { finished in
self.myView.removeFromSuperview()
})
複製程式碼
不推薦:
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
})
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}) { f in
self.myView.removeFromSuperview()
}
複製程式碼
對於上下文清晰的單獨表示式閉包,使用隱式返回:
attendeeList.sort { a, b in
a > b
}
複製程式碼
使用尾隨閉包的鏈式方法應該清晰且在上下文中易讀。作者將自行抉擇空格、換行、命名與匿名引數的使用。舉例:
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)
let value = numbers
.map {$0 * 2}
.filter {$0 > 50}
.map {$0 + 10}
複製程式碼
型別
請儘可能多的使用 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
可以讓你的程式碼避免太多轉換,從而讓你的程式碼更加簡潔。
常量
使用 let
關鍵字來定義常量,使用 var
關鍵字來定義變數。如果變數的值不會改變,則要使用 let
來代替 var
。
提示: 一個比較好的技巧就是定義所有的東西都使用 let
, 當編譯器警告的時候再改為 var
。
你可以在一個型別裡面去定義常量而不是在型別的例項變數中去使用型別屬性。使用 static let
去宣告一個型別屬性作為常量。用這種方式宣告型別屬性比宣告全域性變數更推薦,因為這種方式更能和例項屬性區分開。舉例:
推薦:
enum Math {
static let e = 2.718281828459045235360287
static let root2 = 1.41421356237309504880168872
}
let hypotenuse = side * Math.root2
複製程式碼
注意: 使用無大小寫列舉的優勢,就是它不會被意外的例項化,而只是單純的作為一個名稱空間。
不推薦:
let e = 2.718281828459045235360287 // 汙染全域性名稱空間
let root2 = 1.41421356237309504880168872
let hypotenuse = side * root2 // 什麼 root2?
複製程式碼
靜態方法和可變型別屬性
靜態方法和型別屬性跟全域性函式和全域性變數的工作原理類似,應當謹慎使用。當功能的作用域是一個特定型別或需要與 Objective-C 互動時,它們非常有用。
可選型別
在可接受 nil 值的情況下,使用 ?
宣告變數和函式返回型別為可選型別。
用 !
宣告的隱式解包型別,僅用於稍後在使用前初始化的例項變數,比如將在 viewDidLoad
中建立子檢視。
當訪問一個可選值時,如果值僅被訪問一次或在鏈中有許多可選項時,使用可選鏈:
self.textContainer?.textLabel?.setNeedsDisplay()
複製程式碼
當一次性解包和執行多個操作更方便時,使用可選繫結:
if let textContainer = self.textContainer {
// 用 textContainer 做很多事情
}
複製程式碼
在命名可選變數和屬性時,需避免類似 optionalString
或 maybeView
這樣的命名,因為他們的可選性已經體現在型別宣告中了。
對於可選繫結,適當時使用原始名稱,而不是使用像 unwrappedView
或 actualLabel
這樣的名稱。
推薦:
var subview: UIView?
var volume: Double?
// later on...
if let subview = subview, let volume = volume {
// 使用展開的 subview 和 volume 做某件事
}
複製程式碼
不推薦:
var optionalSubview: UIView?
var volume: Double?
if let unwrappedSubview = optionalSubview {
if let realVolume = volume {
// 使用 unwrappedSubview 和 volume 做某件事
}
}
複製程式碼
延遲初始化
在更細粒度地控制物件宣告週期時考慮使用延遲初始化。 對於UIViewController
,延遲初始化檢視是非常正確的。你也可以直接呼叫 { }()
的閉包或呼叫私有工廠方法。例如:
lazy var locationManager: CLLocationManager = self.makeLocationManager()
private func makeLocationManager() -> CLLocationManager {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}
複製程式碼
注意:
- 因為沒有發生迴圈引用,所以這裡不需要
[unowned self]
。 - 位置管理器對彈出 UI 向使用者申請許可權有副作用,所以細顆粒地控制在這裡是有意義的。
型別推斷
優先選擇簡潔緊湊的程式碼,讓編譯器為單個例項的常量或變數推斷型別。型別推斷也適合於小(非空)的陣列和字典。需要時,請指明特定型別,如 CGFloat
或 Int16
。
推薦:
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5
複製程式碼
不推薦:
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
let names = [String]()
複製程式碼
空陣列和空字典的型別註釋
為空陣列和空字典使用型別註釋。(對於分配給大型、多行文字的陣列和字典,使用型別註釋。)
推薦:
var names: [String] = []
var lookup: [String: Int] = [:]
複製程式碼
不推薦:
var names = [String]()
var lookup = [String: Int]()
複製程式碼
注意:遵循此原則意味著選擇描述性命名比之前更重要。
語法糖
推薦使用型別宣告簡短的版本,而不是完整的泛型語法。
推薦:
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
複製程式碼
不推薦:
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>
複製程式碼
函式 vs 方法
不附屬於類或型別的自有函式應該被謹慎使用。可能的話,首選方法而不是自由函式。這有助於可讀性和易領悟性。
自由函式最適用於它們與任何特定類或例項無關的情況。
推薦:
let sorted = items.mergeSorted() // 容易領悟的
rocket.launch() // 模型的行為
複製程式碼
不推薦:
let sorted = mergeSort(items) // 難以領悟的
launch(&rocket)
複製程式碼
自由函式異常
let tuples = zip(a, b) // 作為自由函式感到自然(對稱)
let value = max(x, y, z) // 另一個感到自然的自由函式
複製程式碼
記憶體管理
程式碼 (甚至非生產環境、教程演示的程式碼)都不應該出現迴圈引用。分析你的物件圖並用 weak
和 unowned
來防止強迴圈引用。或者,使用值型別( struct
、enum
)來徹底防止迴圈引用。
延長物件的生命週期
使用慣用語法 [weak self]
和 guard let strongSelf = self else { return }
來延長物件的生命週期。 在 self
超出閉包生命週期不明顯的地方,[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)
}
複製程式碼
不推薦:
// 如果在響應返回前 self 被釋放,則可能導致崩潰
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
複製程式碼
不推薦:
// 記憶體回收可以發生在更新模型和更新 UI 之間
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}
複製程式碼
訪問控制
在教程中,完整的訪問控制註釋會分散主題且是不必要的。然而,適時地使用 private
和 fileprivate
會使程式碼更加清晰,也會有助於封裝。 在合理情況下,private
要優於 fileprivate
。 使用擴充套件可能會要求你使用 fileprivate
。
只有需要完整的訪問控制規範時,才顯式地使用 open
、 public
和 internal
。
將訪問控制用作前置屬性說明符。僅有 static
說明符或諸如 @IBAction
、 @IBOutlet
和 @discardableResult
的屬性應該放在訪問控制前面。
推薦:
private let message = "Great Scott!"
class TimeMachine {
fileprivate dynamic lazy var fluxCapacitor = FluxCapacitor()
}
複製程式碼
不推薦:
fileprivate let message = "Great Scott!"
class TimeMachine {
lazy dynamic fileprivate var fluxCapacitor = FluxCapacitor()
}
複製程式碼
控制流
優先選擇for
迴圈的 for-in
格式而不是 while-condition-increment
格式。
推薦:
for _ in 0..<3 {
print("Hello three times")
}
for (index, person) in attendeeList.enumerated() {
print("\(person) is at position #\(index)")
}
for index in stride(from: 0, to: items.count, by: 2) {
print(index)
}
for index in (0...3).reversed() {
print(index)
}
複製程式碼
不推薦:
var i = 0
while i < 3 {
print("Hello three times")
i += 1
}
var i = 0
while i < attendeeList.count {
let person = attendeeList[i]
print("\(person) is at position #\(i)")
i += 1
}
複製程式碼
黃金路徑
當使用條件語句編碼時,程式碼的左邊距應該是 「黃金」或「快樂」的路徑。就是不要巢狀 if
語句。多個返回語句是可以的。guard
語句就是因為這個建立的。
Preferred:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
guard let context = context else {
throw FFTError.noContext
}
guard let inputData = inputData else {
throw FFTError.noInputData
}
// 用上下文和輸入計算頻率
return frequencies
}
複製程式碼
不推薦:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
if let context = context {
if let inputData = inputData {
// 用上下文和輸入計算頻率
return frequencies
} else {
throw FFTError.noInputData
}
} else {
throw FFTError.noContext
}
}
複製程式碼
當用 guard
或 if let
解包多個可選值時,在可能的情況下使用最下化複合版本巢狀。舉例:
推薦:
guard let number1 = number1,
let number2 = number2,
let number3 = number3 else {
fatalError("impossible")
}
// 用數字做某事
複製程式碼
不推薦:
if let number1 = number1 {
if let number2 = number2 {
if let number3 = number3 {
// 用數字做某事
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
複製程式碼
失敗防護
對於用某些方法退出,防護語句是必要的。一般地,它應該是一行簡潔的語句,比如: return
、 throw
、 break
、 continue
和 fatalError()
。應該避免大的程式碼塊。如果清理程式碼被用在多個退出點,則可以考慮用 defer
塊來避免清理程式碼的重複。
分號
在 Swift 中,每條程式碼語句後面都不需要加分號。只有在你希望在一行中結合多條語句,才需要加分號。
不要在用分號分隔的單行中寫多條語句。
推薦:
let swift = "not a scripting language"
複製程式碼
不推薦:
let swift = "not a scripting language";
複製程式碼
注:Swift 非常不同於 JavaScript。在 JavaScript 中忽略分號 一般被認為不安全。
括號
條件周圍的括號是不必要的,應該被忽略。
推薦:
if name == "Hello" {
print("World")
}
複製程式碼
不推薦:
if (name == "Hello") {
print("World")
}
複製程式碼
在更大的表示式中,可選括號有時可以讓程式碼讀起來更清晰。
推薦:
let playerMark = (player == current ? "X" : "O")
複製程式碼
組織和包 ID
涉及到 Xcode 專案的地方,組織應該被設定為 Ray Wenderlich
並且包 ID 應該被設定為 com.razeware.TutorialName
,其中 TutorialName
是教程專案的名字。
版權宣告
以下版權宣告應該被包含在每個原始檔的頂部:
/// Copyright (c) 2018 Razeware LLC
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
/// distribute, sublicense, create a derivative work, and/or sell copies of the
/// Software in any work that is designed, intended, or marketed for pedagogical or
/// instructional purposes related to programming, coding, application development,
/// or information technology. Permission for such use, copying, modification,
/// merger, publication, distribution, sublicensing, creation of derivative works,
/// or sale is expressly withheld.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
/// THE SOFTWARE.
複製程式碼
笑臉
笑臉是網站 raywenderlich.com 非常突出的風格特點!正確使用微笑來表達對編碼主題的歡樂與興奮是非常重要的。使用右方括號 ]
是因為它代表 ASCII 中的最大笑容。右括號 )
表示三心二意的笑臉,因此不推薦使用。
推薦:
:]
複製程式碼
不推薦:
:)
複製程式碼
參考文獻
iOSCaff 是一個面向 iOS 開發者的技術知識社群,致力於為開發者提供一個更加高效、便捷的學習環境。