Swift Style Guide
Swift代碼規(guī)范指南
Make sure to read Apple's API Design Guidelines.
確保閱讀蘋果的API設(shè)計(jì)指南旷祸。
Specifics from these guidelines + additional remarks are mentioned below.
下文提到了這些指南中的具體細(xì)節(jié)+補(bǔ)充意見涧黄。
This guide was last updated for Swift 4.0 on February 14, 2018.
本指南最后一次針對(duì)Swift 4.0更新是在2018年2月14日。
Table Of Contents
目錄
- Swift Style Guide
- Swift代碼規(guī)范指南
- Code Formatting
- 代碼格式
- Naming
- 命名
- Coding Style
-
- 編碼規(guī)范
- 3.1 General
- 3.1 概述
- 3.2 Access Modifiers
- 3.2 訪問修飾符
- 3.3 Custom Operators
- 3.3 自定義操作符
- 3.4 Switch Statements and
enum
s - 3.4 Switch語句和枚舉
- 3.5 Optionals
- 3.5 可選類型
- 3.6 Protocols
- 3.6 協(xié)議
- 3.7 Properties
- 3.7 屬性
- 3.8 Closures
- 3.8 閉包
- 3.9 Arrays
- 3.9 數(shù)組
- 3.10 Error Handling
- 3.10 錯(cuò)誤處理
- 3.11 Using
guard
Statements - 3.11 使用
guard
語句
- Documentation/Comments
-
- 文檔/注釋
- 4.1 Documentation
- 4.1 文檔
- 4.2 Other Commenting Guidelines
- 4.2 其他注釋原則
1. Code Formatting
1. 代碼格式
1.1 Use 4 spaces for tabs.
1.1 使用四個(gè)空格進(jìn)行縮進(jìn)识藤。
1.2 Avoid uncomfortably long lines with a hard maximum of 160 characters per line (Xcode->Preferences->Text Editing->Page guide at column: 160 is helpful for this)1.2 每行最多160個(gè)字符邪狞,這樣可以避免一行過長(zhǎng) (Xcode->Preferences->Text Editing->Page guide at column: 設(shè)置成160行即可)
譯者注:大可不必設(shè)置祷蝌,自己API設(shè)計(jì)好一點(diǎn)即可,調(diào)用他人的API也無法避免行數(shù)過長(zhǎng)帆卓,真的超了設(shè)置也起不了什么作用巨朦,再說Xcode本身有自動(dòng)換行米丘。
1.3 Ensure that there is a newline at the end of every file.1.3 確保每個(gè)文件結(jié)尾都有空白行。
譯者注:Xcode12開始糊啡,每個(gè)文件結(jié)尾都自動(dòng)有偏移量了拄查,開發(fā)者自己不必加空行。
- 1.4 Ensure that there is no trailing whitespace anywhere (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines).
- 1.4 確保每行都不以空白字符作為結(jié)尾 (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines)悔橄。
譯者注:這樣能避免修改代碼過程中因多個(gè)空格靶累、少個(gè)空格而產(chǎn)生不必要的文件差異。如果項(xiàng)目里的每個(gè)開發(fā)人員都設(shè)置最好癣疟,否則只要有人不設(shè)置,他提交的代碼還是會(huì)出現(xiàn)結(jié)尾有空白字符潮酒,而其他設(shè)置過的人觸碰到該行代碼睛挚,空格自動(dòng)消失,產(chǎn)生的差異還無法回退急黎。
- 1.5 Do not place opening braces on new lines - we use the 1TBS style.
- 1.5 左大括號(hào)不用另起一行扎狱。
class SomeClass {
func someMethod() {
if x == y {
/* ... */
} else if x == z {
/* ... */
} else {
/* ... */
}
}
/* ... */
}
- 1.6 When writing a type for a property, constant, variable, a key for a dictionary, a function argument, a protocol conformance, or a superclass, don't add a space before the colon.
- 1.6 當(dāng)在寫一個(gè)變量類型,一個(gè)字典里的主鍵勃教,一個(gè)函數(shù)的參數(shù)淤击,遵從一個(gè)協(xié)議,或一個(gè)父類故源,不用在分號(hào)前添加空格污抬。
// specifying type
let pirateViewController: PirateViewController
// dictionary syntax (note that we left-align as opposed to aligning colons)
let ninjaDictionary: [String: AnyObject] = [
"fightLikeDairyFarmer": false,
"disgusting": true
]
// declaring a function
func myFunction<T, U: SomeProtocol>(firstArgument: U, secondArgument: T) where T.RelatedType == U {
/* ... */
}
// calling a function
someFunction(someArgument: "Kitten")
// superclasses
class PirateViewController: UIViewController {
/* ... */
}
// protocols
extension PirateViewController: UITableViewDataSource {
/* ... */
}
- 1.7 In general, there should be a space following a comma.
- 1.7 通常來說,要在逗號(hào)后面加空格绳军。
let myArray = [1, 2, 3, 4, 5]
-
1.8 There should be a space before and after a binary operator such as
+
,==
, or->
. There should also not be a space after a(
and before a)
. -
1.8 在二元運(yùn)算符前后要加空格印机,例如
+
,==
, 或->
。而在左括號(hào)(
后面和有括號(hào))
的前面不要加空格门驾。
let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
fatalError("The universe is broken.")
}
func pancake(with syrup: Syrup) -> Pancake {
/* ... */
}
- 1.9 We follow Xcode's recommended indentation style (i.e. your code should not change if CTRL-I is pressed). When declaring a function that spans multiple lines, prefer using that syntax to which Xcode, as of version 7.3, defaults.
- 1.9 遵守Xcode內(nèi)置的縮進(jìn)格式 (如果已經(jīng)遵守,按下Control+i鍵,代碼格式不會(huì)有變化). 當(dāng)聲明的一個(gè)函數(shù)需要跨多行時(shí)堰燎,推薦使用Xcode默認(rèn)的格式聚磺,目前Xcode 版本是 7.3。
// Xcode indentation for a function declaration that spans multiple lines
func myFunctionWithManyParameters(parameterOne: String,
parameterTwo: String,
parameterThree: String) {
// Xcode indents to here for this kind of statement
print("\(parameterOne) \(parameterTwo) \(parameterThree)")
}
// Xcode indentation for a multi-line `if` statement
if myFirstValue > (mySecondValue + myThirdValue)
&& myFourthValue == .someEnumValue {
// Xcode indents to here for this kind of statement
print("Hello, World!")
}
1.10 When calling a function that has many parameters, put each argument on a separate line with a single extra indentation.1.10 當(dāng)調(diào)用的函數(shù)有多個(gè)參數(shù)時(shí)聂沙,每一個(gè)參數(shù)另起一行秆麸,并比函數(shù)名多一個(gè)縮進(jìn)。
someFunctionWithManyArguments(
firstArgument: "Hello, I am a string",
secondArgument: resultFromSomeFunction(),
thirdArgument: someOtherLocalProperty)
譯者注:其實(shí)我個(gè)人不建議參數(shù)換行逐纬,除非參數(shù)需要突出讓閱讀者注意蛔屹,否則適得其反,總之慎用豁生!慎用漫贞!慎用龟梦!
很多開發(fā)者不僅喜歡參數(shù)換行聘鳞,并且第一個(gè)參數(shù)不換行,格式對(duì)齊后糕再,整個(gè)代碼塊擠在了右邊一小塊究抓,凌亂不堪猾担,閱讀起來真難受。
什么鬼刺下?這種代碼都寫得出...
所有參數(shù)都換行绑嘹,比上面好看很多。
個(gè)人更傾向于不換行橘茉,縱向閱讀容易理解這里主要就兩句代碼工腋,一是獲取股票持倉者,通過閉包回調(diào)畅卓,二是閉包內(nèi)處理回調(diào)數(shù)據(jù)擅腰。
有必要的情況下可以使用參數(shù)換行,但一定不要出現(xiàn)第一張圖那種凌亂不堪的代碼翁潘。
-
1.11 When dealing with an implicit array or dictionary large enough to warrant splitting it into multiple lines, treat the
[
and]
as if they were braces in a method,if
statement, etc. Closures in a method should be treated similarly. -
1.11 當(dāng)遇到需要處理的數(shù)組或字典內(nèi)容較多需要多行顯示時(shí)趁冈,需把
[
和]
類似于方法體里的括號(hào), 方法體里的閉包也要做類似處理拜马。
someFunctionWithABunchOfArguments(
someStringArgument: "hello I am a string",
someArrayArgument: [
"dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
"string one is crazy - what is it thinking?"
],
someDictionaryArgument: [
"dictionary key 1": "some value 1, but also some more text here",
"dictionary key 2": "some value 2"
],
someClosure: { parameter1 in
print(parameter1)
})
- 1.12 Prefer using local constants or other mitigation techniques to avoid multi-line predicates where possible.
- 1.12 應(yīng)盡量避免出現(xiàn)多行斷言渗勘,可使用本地變量或其他策略沐绒。
// PREFERRED
let firstCondition = x == firstReallyReallyLongPredicateFunction()
let secondCondition = y == secondReallyReallyLongPredicateFunction()
let thirdCondition = z == thirdReallyReallyLongPredicateFunction()
if firstCondition && secondCondition && thirdCondition {
// do something
}
// NOT PREFERRED
if x == firstReallyReallyLongPredicateFunction()
&& y == secondReallyReallyLongPredicateFunction()
&& z == thirdReallyReallyLongPredicateFunction() {
// do something
}
2. Naming
2. 命名
-
2.1 There is no need for Objective-C style prefixing in Swift (e.g. use just
GuybrushThreepwood
instead ofLIGuybrushThreepwood
). - 2.1 在Swift中不用如Objective-C式 一樣添加前綴 (如使用 GuybrushThreepwoode 而不是 LIGuybrushThreepwood)。
譯者注:還是有很多人寫Swift習(xí)慣用前綴呀邢,那是因?yàn)闆]有明白Swift的真諦洒沦,Swift不同框架即便用同個(gè)類名也不會(huì)沖突,在同一個(gè)文件中使用到二者价淌,需要引用對(duì)應(yīng)框架名來區(qū)分申眼。
2.2 Use
PascalCase
for type names (e.g.struct
,enum
,class
,typedef
,associatedtype
, etc.).2.2 使用帕斯卡拼寫法(又名
大駱駝拼寫法
,首字母大寫
)為類型
命名 (如struct
,enum
,class
,typedef
,associatedtype
等)蝉衣。2.3 Use
camelCase
(initial lowercase letter) for function, method, property, constant, variable, argument names, enum cases, etc.2.3 使用
小駱駝拼寫法
(首字母小寫
) 為函數(shù)
括尸,方法
,變量
病毡,常量
濒翻,參數(shù)
等命名。2.4 When dealing with an acronym or other name that is usually written in all caps, actually use all caps in any names that use this in code. The exception is if this word is at the start of a name that needs to start with lowercase - in this case, use all lowercase for the acronym.
2.4 首字母縮略詞在命名中一般來說都是全部大寫啦膜,例外的情形是如果首字母縮略詞是一個(gè)命名的開始部分有送,而這個(gè)命名需要小寫字母作為開頭,這種情形下首字母縮略詞全部小寫僧家。
// "HTML" is at the start of a constant name, so we use lowercase "html"
let htmlBodyContent: String = "<p>Hello, World!</p>"
// Prefer using ID to Id
let profileID: Int = 1
// Prefer URLFinder to UrlFinder
class URLFinder {
/* ... */
}
-
2.5 All constants that are instance-independent should be
static
. All suchstatic
constants should be placed in a marked section of theirclass
,struct
, orenum
. For classes with many constants, you should group constants that have similar or the same prefixes, suffixes and/or use cases. -
2.5 所有與實(shí)例無關(guān)的常量都應(yīng)該是
static
雀摘。所有這些static
常數(shù)都應(yīng)放置在其class
、struct
或enum
的標(biāo)記部分八拱。對(duì)于具有許多常量的類阵赠,您應(yīng)該對(duì)具有相似或相同前綴、后綴和/或用例的常量進(jìn)行分組肌稻。
// PREFERRED
class MyClassName {
// MARK: - Constants
static let buttonPadding: CGFloat = 20.0
static let indianaPi = 3
static let shared = MyClassName()
}
// NOT PREFERRED
class MyClassName {
// Don't use `k`-prefix
static let kButtonPadding: CGFloat = 20.0
// Don't namespace constants
enum Constant {
static let indianaPi = 3
}
}
譯者注:蘋果代碼規(guī)范指南里每一點(diǎn)都有其意義及精髓存在清蚀,需要用心去體會(huì)其設(shè)計(jì)意義。否則每個(gè)人都按自己的方式爹谭,或者沿用其他語言的習(xí)慣來寫枷邪,代碼將風(fēng)格迥異。我甚至看到有一篇譯文旦棉,枉顧蘋果的代碼示例齿风,固執(zhí)己見推薦使用k作為前綴。k作為前綴是OC的宏定義的寫法绑洛,在Swift中不推薦這種寫法救斑。
-
2.6 For generics and associated types, use a
PascalCase
word that describes the generic. If this word clashes with a protocol that it conforms to or a superclass that it subclasses, you can append aType
suffix to the associated type or generic name. -
2.6 對(duì)于泛型和相關(guān)類型,請(qǐng)使用
PascalCase
單詞(大駝峰式命名)描述泛型真屯。如果此詞與其遵循的協(xié)議名
或繼承類的超類名
沖突脸候,您可以在關(guān)聯(lián)類型或泛型名稱中添加Type后綴。
class SomeClass<Model> { /* ... */ }
protocol Modelable {
associatedtype Model
}
protocol Sequence {
associatedtype IteratorType: Iterator
}
- 2.7 Names should be descriptive and unambiguous.
- 2.7 姓名應(yīng)具有描述性和明確性。
// PREFERRED
class RoundAnimatingButton: UIButton { /* ... */ }
// NOT PREFERRED
class CustomButton: UIButton { /* ... */ }
- 2.8 Do not abbreviate, use shortened names, or single letter names.
- 2.8 不要縮寫运沦、簡(jiǎn)寫命名或用單個(gè)字母命名泵额。
// PREFERRED
class RoundAnimatingButton: UIButton {
let animationDuration: NSTimeInterval
func startAnimating() {
let firstSubview = subviews.first
}
}
// NOT PREFERRED
class RoundAnimating: UIButton {
let aniDur: NSTimeInterval
func srtAnmating() {
let v = subviews.first
}
}
- 2.9 Include type information in constant or variable names when it is not obvious otherwise.
- 2.9 當(dāng)常量或變量名稱不能明顯地表明類型時(shí),應(yīng)當(dāng)將類型信息包含在命名中携添。
// PREFERRED
class ConnectionTableViewCell: UITableViewCell {
let personImageView: UIImageView
let animationDuration: TimeInterval
// it is ok not to include string in the ivar name here because it's obvious
// that it's a string from the property name
let firstName: String
// though not preferred, it is OK to use `Controller` instead of `ViewController`
let popupController: UIViewController
let popupViewController: UIViewController
// when working with a subclass of `UIViewController` such as a table view
// controller, collection view controller, split view controller, etc.,
// fully indicate the type in the name.
let popupTableViewController: UITableViewController
// when working with outlets, make sure to specify the outlet type in the
// property name.
@IBOutlet weak var submitButton: UIButton!
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var nameLabel: UILabel!
}
// NOT PREFERRED
class ConnectionTableViewCell: UITableViewCell {
// this isn't a `UIImage`, so shouldn't be called image
// use personImageView instead
let personImage: UIImageView
// this isn't a `String`, so it should be `textLabel`
let text: UILabel
// `animation` is not clearly a time interval
// use `animationDuration` or `animationTimeInterval` instead
let animation: TimeInterval
// this is not obviously a `String`
// use `transitionText` or `transitionString` instead
let transition: String
// this is a view controller - not a view
let popupView: UIViewController
// as mentioned previously, we don't want to use abbreviations, so don't use
// `VC` instead of `ViewController`
let popupVC: UIViewController
// even though this is still technically a `UIViewController`, this property
// should indicate that we are working with a *Table* View Controller
let popupViewController: UITableViewController
// for the sake of consistency, we should put the type name at the end of the
// property name and not at the start
@IBOutlet weak var btnSubmit: UIButton!
@IBOutlet weak var buttonSubmit: UIButton!
// we should always have a type in the property name when dealing with outlets
// for example, here, we should have `firstNameLabel` instead
@IBOutlet weak var firstName: UILabel!
}
譯者注:許多開發(fā)人員在屬性命名方面真的很隨意嫁盲,也不學(xué)學(xué)蘋果系統(tǒng)框架中是怎么命名的,讓其他開發(fā)閱讀起來有多累烈掠!
2.10 When naming function arguments, make sure that the function can be read easily to understand the purpose of each argument.
2.10 命名函數(shù)參數(shù)時(shí)羞秤,請(qǐng)確保函數(shù)易于閱讀以理解每個(gè)參數(shù)的用途。
2.11 As per Apple's API Design Guidelines, a
protocol
should be named as nouns if they describe what something is doing (e.g.Collection
) and using the suffixesable
,ible
, oring
if it describes a capability (e.g.Equatable
,ProgressReporting
). If neither of those options makes sense for your use case, you can add aProtocol
suffix to the protocol's name as well. Some exampleprotocol
s are below.2.11 根據(jù)蘋果的API設(shè)計(jì)指南左敌,如果
protocol
描述了正在做的事情(例如Collection
)并使用后綴able
瘾蛋、ible
或ing
如果它描述了一種功能(例如Equatable
,ProgressReporting
)矫限。如果這兩個(gè)選項(xiàng)對(duì)您的用例都毫無意義哺哼,您也可以在協(xié)議名稱中添加協(xié)議后綴。以下是一些協(xié)議示例叼风。
// here, the name is a noun that describes what the protocol does
protocol TableViewSectionProvider {
func rowHeight(at row: Int) -> CGFloat
var numberOfRows: Int { get }
/* ... */
}
// here, the protocol is a capability, and we name it appropriately
protocol Loggable {
func logCurrentState()
/* ... */
}
// suppose we have an `InputTextView` class, but we also want a protocol
// to generalize some of the functionality - it might be appropriate to
// use the `Protocol` suffix here
protocol InputTextViewProtocol {
func sendTrackingEvent()
func inputText() -> String
/* ... */
}
3. Coding Style
3. 編碼風(fēng)格
3.1 General
3.1 概述
3.1.1 Prefer
let
tovar
whenever possible.3.1.1 盡可能使用
let
(能用let
就不要用var
)3.1.2 Prefer the composition of
map
,filter
,reduce
, etc. over iterating when transforming from one collection to another. Make sure to avoid using closures that have side effects when using these methods.3.1.2 從一個(gè)集合轉(zhuǎn)換為另一個(gè)集合時(shí)取董,推薦使用
map
、filter
无宿、reduce
等甲葬。從一個(gè)集合轉(zhuǎn)換到另一個(gè)集合時(shí)過度迭代。使用這些方法時(shí)懈贺,請(qǐng)確保避免使用有副作用的閉包。
// PREFERRED
let stringOfInts = [1, 2, 3].flatMap { String($0) }
// ["1", "2", "3"]
// NOT PREFERRED
var stringOfInts: [String] = []
for integer in [1, 2, 3] {
stringOfInts.append(String(integer))
}
// PREFERRED
let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }
// [4, 8, 16, 42]
// NOT PREFERRED
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
if integer % 2 == 0 {
evenNumbers.append(integer)
}
}
- 3.1.3 Prefer not declaring types for constants or variables if they can be inferred anyway.
- 3.1.3 如果可以推斷出常量或變量類型坡垫,最好不要聲明它們的類型梭灿。
// 推薦
let url = "xxx"
let port = 8080
// 不推薦
let url: String = "xxx"
let port: Int = 8080
-
3.1.4 If a function returns multiple values, prefer returning a tuple to using
inout
arguments (it’s best to use labeled tuples for clarity on what you’re returning if it is not otherwise obvious). If you use a certain tuple more than once, consider using atypealias
. If you’re returning 3 or more items in a tuple, consider using astruct
orclass
instead. -
3.1.4 如果函數(shù)返回多個(gè)值,傾向于返回
元組
冰悠,而不是使用inout
參數(shù)(如果元組的項(xiàng)的含義不明顯堡妒,最好使用帶標(biāo)簽的元組來明確要返回的內(nèi)容)。如果您多次使用某個(gè)元組溉卓,請(qǐng)考慮使用typealias皮迟。如果您要以元組返回3個(gè)或更多項(xiàng)目,請(qǐng)考慮改用struct
或class
桑寨。
func pirateName() -> (firstName: String, lastName: String) {
return ("Guybrush", "Threepwood")
}
let name = pirateName()
let firstName = name.firstName
let lastName = name.lastName
3.1.5 Be wary of retain cycles when creating delegates/protocols for your classes; typically, these properties should be declared
weak
.3.1.5 為類創(chuàng)建委托/協(xié)議時(shí)伏尼,請(qǐng)小心保留周期;通常尉尾,這些屬性應(yīng)聲明為
weak
爆阶。3.1.6 Be careful when calling
self
directly from an escaping closure as this can cause a retain cycle - use a capture list when this might be the case:
3.1.6 直接從轉(zhuǎn)義閉包調(diào)用self
時(shí)要小心,因?yàn)檫@可能導(dǎo)致保留循環(huán) - 在這種情況下使用捕獲列表:
myFunctionWithEscapingClosure() { [weak self] (error) -> Void in
// you can do this
self?.doSomething()
// or you can do this
guard let strongSelf = self else {
return
}
strongSelf.doSomething()
}
譯者注:Swift4.2之后建議這么寫
guard let self = self else { return }
見:https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md#relying-on-a-compiler-bug
3.1.7 Don't use labeled breaks.
3.1.7 Switch 模塊中不用顯式使用
break
。3.1.8 Don't place parentheses around control flow predicates.
3.1.8 控制流斷言不用加小括號(hào)辨图。
// PREFERRED
if x == y {
/* ... */
}
// NOT PREFERRED
if (x == y) {
/* ... */
}
-
3.1.9 Avoid writing out an
enum
type where possible - use shorthand. - 3.1.9 使用枚舉時(shí)班套,盡可能不要把類型寫出來。
// PREFERRED
imageView.setImageWithURL(url, type: .person)
// NOT PREFERRED
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)
3.1.10 Don’t use shorthand for class methods since it is generally more difficult to infer the context from class methods as opposed toenum
s.3.1.10 不要對(duì)類方法使用簡(jiǎn)寫故河,因?yàn)閺念惙椒ㄍ茢嗌舷挛耐ǔ1让杜e更難吱韭。
// PREFERRED
imageView.backgroundColor = UIColor.white
// NOT PREFERRED
imageView.backgroundColor = .white
譯者注:這里我覺得UIColor類方法簡(jiǎn)寫還是能使代碼更簡(jiǎn)潔的,我習(xí)慣簡(jiǎn)寫鱼的,但其他類方法還是看具體而定理盆,如果簡(jiǎn)寫不能很好地推斷上下文,那還是建議加上類名為好鸳吸。
3.1.11 Prefer not writing
self.
unless it is required.3.1.11 盡量不要使用
self.
熏挎,除非必要(閉包內(nèi)必須使用self.
)。3.1.12 When writing methods, keep in mind whether the method is intended to be overridden or not. If not, mark it as
final
, though keep in mind that this will prevent the method from being overwritten for testing purposes. In general,final
methods result in improved compilation times, so it is good to use this when applicable. Be particularly careful, however, when applying thefinal
keyword in a library since it is non-trivial to change something to be non-final
in a library as opposed to have changing something to be non-final
in your local project.3.1.12 在編寫方法時(shí)晌砾,需要衡量該方法是否將來會(huì)被重寫坎拐,如果不希望被子類重寫,請(qǐng)使用
final
關(guān)鍵詞修飾养匈。一般來說哼勇,final
修飾的方法可以得到編譯速度的優(yōu)化,在有必要時(shí)呕乎,使用final
是不錯(cuò)的選擇积担。但特別要注意的是,相比在本地項(xiàng)目來說猬仁,在庫中將final
改為非final
并非易事帝璧。3.1.13 When using a statement such as
else
,catch
, etc. that follows a block, put this keyword on the same line as the block. Again, we are following the 1TBS style here. Exampleif
/else
anddo
/catch
code is below.3.1.13 在代碼塊后面使用
else
,catch
等語句時(shí),將關(guān)鍵詞與代碼塊放在同一行湿刽。我們?cè)谶@里遵循 1TBS style的烁。下面有if
/else
anddo
/catch
的示例。
if someBoolean {
// do something
} else {
// do something else
}
do {
let fileContents = try readFile("filename.txt")
} catch {
print(error)
}
3.1.14 Prefer
static
toclass
when declaring a function or property that is associated with a class as opposed to an instance of that class. Only useclass
if you specifically need the functionality of overriding that function or property in a subclass, though consider using aprotocol
to achieve this instead.3.1.14 在聲明與類相關(guān)聯(lián)的函數(shù)或?qū)傩远皇窃擃惖膶?shí)例時(shí)诈闺,優(yōu)先使用
static
而非class
渴庆。 僅當(dāng)您特別需要在子類中覆蓋該函數(shù)或?qū)傩缘墓δ軙r(shí)才使用class
,盡管考慮使用protocol
來實(shí)現(xiàn)這一點(diǎn)雅镊。3.1.15 If you have a function that takes no arguments, has no side effects, and returns some object or value, prefer using a computed property instead.
3.1.15 如果您的函數(shù)沒有參數(shù)襟雷,沒有副作用,并返回一些對(duì)象或值仁烹,則更喜歡使用計(jì)算屬性耸弄。
譯者注:這里要好好理解
副作用
的意思,是指一些不應(yīng)該有的卓缰,或讓人意料之外的作用叙赚。下面我列舉一些代碼示例老客。
// 推薦
struct Square {
var side: CGFloat = 1.0
// 獲取周長(zhǎng),使用計(jì)算屬性
var girth: CGFloat {
return side * 4
}
}
// 不推薦
struct Square {
var side: CGFloat = 1.0
// 無參數(shù)震叮、無`副作用`胧砰,返回周長(zhǎng),不推薦使用函數(shù)
func girth() -> CGFloat {
return side * 4
}
}
// 推薦
class Square {
var side: CGFloat = 1.0
// 將邊長(zhǎng)縮小一半苇瓣,并返回縮小后的周長(zhǎng)尉间,使用函數(shù)
func halfGirth() -> CGFloat {
self.side = side * 0.5
return side * 4
}
}
// 不推薦
class Square {
var side: CGFloat = 1.0
// 這里存在`副作用`,讓人意想不到击罪,不推薦使用計(jì)算屬性
var halfGirth: CGFloat {
self.side = side * 0.5
return side * 4
}
}
3.2 Access Modifiers
3.2 訪問修飾符
- 3.2.1 Write the access modifier keyword first if it is needed.
- 3.2.1 如果需要哲嘲,將訪問修飾符放在第一位。
// PREFERRED
private static let myPrivateNumber: Int
// NOT PREFERRED
static private let myPrivateNumber: Int
- 3.2.2 The access modifier keyword should not be on a line by itself - keep it inline with what it is describing.
- 3.2.2 訪問修飾符不應(yīng)單獨(dú)成行媳禁,應(yīng)與修飾的內(nèi)容放在同一行眠副。
// PREFERRED
open class Pirate {
/* ... */
}
// NOT PREFERRED
open
class Pirate {
/* ... */
}
3.2.3 In general, do not write the
internal
access modifier keyword since it is the default.3.2.3 一般來說,不要編寫
internal
訪問修飾符關(guān)鍵字竣稽,因?yàn)樗悄J(rèn)的囱怕。3.2.4 If a property needs to be accessed by unit tests, you will have to make it
internal
to use@testable import ModuleName
. If a property should be private, but you declare it to beinternal
for the purposes of unit testing, make sure you add an appropriate bit of documentation commenting that explains this. You can make use of the- warning:
markup syntax for clarity as shown below.3.2.4 如果需要通過單元測(cè)試訪問屬性,在單元測(cè)試文件中使用
@testable import ModuleName
毫别,以此來訪問聲明為internal
的類型娃弓。如果一個(gè)屬性應(yīng)該是private
的,但為了單元測(cè)試的目的岛宦,您聲明它是internal
的台丛,請(qǐng)確保添加適當(dāng)?shù)奈臋n注釋來解釋這一點(diǎn)。您可以使用- warning:
標(biāo)記語法來保持清晰砾肺,如下所示挽霉。
/**
This property defines the pirate's name.
- warning: Not `private` for `@testable`.
*/
let pirateName = "LeChuck"
3.2.5 Prefer
private
tofileprivate
where possible.3.2.5 能用
private
的,就不要用fileprivate
变汪。3.2.6 When choosing between
public
andopen
, preferopen
if you intend for something to be subclassable outside of a given module andpublic
otherwise. Note that anythinginternal
and above can be subclassed in tests by using@testable import
, so this shouldn't be a reason to useopen
. In general, lean towards being a bit more liberal with usingopen
when it comes to libraries, but a bit more conservative when it comes to modules in a codebase such as an app where it is easy to change things in multiple modules simultaneously.3.2.6 在
public
和open
之間怎么選擇炼吴?如果您打算將某些東西在給定模塊之外進(jìn)行子類化時(shí),使用open
疫衩,否則使用public
。請(qǐng)注意荣德,任何internal
或以上級(jí)別的內(nèi)容闷煤,在單元測(cè)試中都可以通過@testable import
來使用,無需將其聲明為open
涮瞻。一般來說鲤拿,在庫方面,傾向于更自由地使用open
署咽,但像app這種可以輕松地同時(shí)修改多個(gè)模塊的模塊中近顷,則傾向于更保守一點(diǎn)生音。
3.3 Custom Operators
3.3 自定義操作符
Prefer creating named functions to custom operators.
不推薦使用自定義操作符,如果需要?jiǎng)?chuàng)建函數(shù)來替代窒升。
If you want to introduce a custom operator, make sure that you have a very good reason why you want to introduce a new operator into global scope as opposed to using some other construct.
如果您想引入自定義運(yùn)算符缀遍,請(qǐng)確保您有很好的理由,為什么您希望將新運(yùn)算符引入全局范圍饱须,而不使用其他構(gòu)造域醇。
You can override existing operators to support new types (especially ==
). However, your new definitions must preserve the semantics of the operator. For example, ==
must always test equality and return a boolean.
您可以重寫已存在的運(yùn)算符來支持新的類型(特別是==
),但無論如何蓉媳,你都得保留運(yùn)算符原來的語義譬挚,例如,==
必須是用來測(cè)試是否相等并返回布爾值酪呻。
譯者注:相信很多人也不會(huì)去重寫運(yùn)算符减宣,更多的是使用自定義方法芜赌,反正不要去踩這個(gè)坑锄禽。
3.4 Switch Statements and enum
s
3.4 Switch語句和enum
3.4.1 When using a switch statement that has a finite set of possibilities (
enum
), do NOT include adefault
case. Instead, place unused cases at the bottom and use thebreak
keyword to prevent execution.3.4.1 使用有限集合(
enum
)的switch
語句時(shí)慕趴,不要包含default
顿痪。反之亡问,switch
語句的末尾用default
涵蓋其他情況闷沥。3.4.2 Since
switch
cases in Swift break by default, do not include thebreak
keyword if it is not needed.3.4.2
switch
語句在Swift中默認(rèn)是會(huì)break的父虑,因此不需要添加break
(OC中需要在每個(gè)case末尾添加break)单山。3.4.3 The
case
statements should line up with theswitch
statement itself as per default Swift standards.3.4.3 根據(jù)默認(rèn)Swift標(biāo)準(zhǔn)眼溶,
case
語句應(yīng)與switch
本身對(duì)齊悠砚。3.4.4 When defining a case that has an associated value, make sure that this value is appropriately labeled as opposed to just types (e.g.
case hunger(hungerLevel: Int)
instead ofcase hunger(Int)
).3.4.4 在定義有關(guān)聯(lián)值的
case
時(shí),請(qǐng)確保該值有恰當(dāng)?shù)拿Q堂飞,而不只有類型(例如灌旧,case hunger(hungerLevel: Int)
而不是case hunger(Int)
)。
enum Problem {
case attitude
case hair
case hunger(hungerLevel: Int)
}
func handleProblem(problem: Problem) {
switch problem {
case .attitude:
print("At least I don't have a hair problem.")
case .hair:
print("Your barber didn't know when to stop.")
case .hunger(let hungerLevel):
print("The hunger level is \(hungerLevel).")
}
}
3.4.5 Prefer lists of possibilities (e.g.
case 1, 2, 3:
) to using thefallthrough
keyword where possible).3.4.5 推薦使用多個(gè)可能性(例如
case 1, 2, 3:
)绰筛,而不用fallthrough
枢泰。3.4.6 If you have a default case that shouldn't be reached, preferably throw an error (or handle it some other similar way such as asserting).
3.4.6 如果
default
的情況不應(yīng)該被觸發(fā),最好拋出錯(cuò)誤(或以其他類似方式處理它铝噩,例如斷言)衡蚂。
func handleDigit(_ digit: Int) throws {
switch digit {
case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9:
print("Yes, \(digit) is a digit!")
default:
throw Error(message: "The given number was not a digit.")
}
}
3.5 Optionals
3.5 可選
3.5.1 The only time you should be using implicitly unwrapped optionals is with
@IBOutlet
s. In every other case, it is better to use a non-optional or regular optional property. Yes, there are cases in which you can probably "guarantee" that the property will never benil
when used, but it is better to be safe and consistent. Similarly, don't use force unwraps.3.5.1 唯一應(yīng)該使用
隱式解包可選類型
的場(chǎng)景是與@IBOutlet結(jié)合使用。在所有其他情況下骏庸,最好使用非可選
或顯式可選屬性
(這樣可以保持一致性和程序更加健壯)毛甲。雖然在某些情況下,您可能可以“保證”屬性在使用時(shí)永遠(yuǎn)不會(huì)為nil具被,但最好是安全和一致玻募。同樣,不要強(qiáng)制解包一姿。3.5.2 Don't use
as!
ortry!
.3.5.2 不要使用
as!
或try!
3.5.3 If you don't plan on actually using the value stored in an optional, but need to determine whether or not this value is
nil
, explicitly check this value againstnil
as opposed to usingif let
syntax.3.5.3 如果您不打算可選類型中存儲(chǔ)的值七咧,只是需要判斷值是否為
nil
跃惫,應(yīng)直接將其與nil
比較,而不是使用if let
語法艾栋。
// PREFERRED
if someOptional != nil {
// do something
}
// NOT PREFERRED
if let _ = someOptional {
// do something
}
-
3.5.4 Don't use
unowned
. You can think ofunowned
as somewhat of an equivalent of aweak
property that is implicitly unwrapped (thoughunowned
has slight performance improvements on account of completely ignoring reference counting). Since we don't ever want to have implicit unwraps, we similarly don't wantunowned
properties. -
3.5.4 不要使用
unowned
爆存。您可以將unowned
屬性看做是一個(gè)強(qiáng)制解包的weak
屬性(盡管unowned
是完全忽略引用計(jì)數(shù),性能略有提升)裹粤。由于我們不推薦強(qiáng)制解包终蒂,因此同樣不推薦使用unowned
屬性。
// PREFERRED
weak var parentViewController: UIViewController?
// NOT PREFERRED
weak var parentViewController: UIViewController!
unowned var parentViewController: UIViewController
- 3.5.5 When unwrapping optionals, use the same name for the unwrapped constant or variable where appropriate.
- 3.5.5 對(duì)可選屬性解包時(shí)遥诉,對(duì)解開的常量或變量使用相同的名稱拇泣。
guard let myValue = myValue else {
return
}
-
3.5.6 Use
XCTUnwrap
instead of forced unwrapping in tests. -
3.5.6 在單元測(cè)試中,對(duì)可選類型使用
XCTUnwrap
矮锈,而不用強(qiáng)制解包霉翔。
func isEvenNumber(_ number: Int) -> Bool {
return number % 2 == 0
}
// PREFERRED
func testWithXCTUnwrap() throws {
let number: Int? = functionThatReturnsOptionalNumber()
XCTAssertTrue(isEvenNumber(try XCTUnwrap(number)))
}
// NOT PREFERRED
func testWithForcedUnwrap() {
let number: Int? = functionThatReturnsOptionalNumber()
XCTAssertTrue(isEvenNumber(number!)) // may crash the simulator
}
3.6 Protocols
3.6 協(xié)議
When implementing protocols, there are two ways of organizing your code:
實(shí)現(xiàn)協(xié)議時(shí),有兩種方式來組織代碼:
- Using
// MARK:
comments to separate your protocol implementation from the rest of your code - Using an extension outside your
class
/struct
implementation code, but in the same source file - 使用
// MARK:
注釋將您的協(xié)議實(shí)現(xiàn)與代碼的其余部分分開 - 在
class
/struct
實(shí)現(xiàn)代碼之外使用擴(kuò)展名苞笨,但在同一源代碼文件中
Keep in mind that when using an extension, however, the methods in the extension can't be overridden by a subclass, which can make testing difficult. If this is a common use case, it might be better to stick with method #1 for consistency. Otherwise, method #2 allows for cleaner separation of concerns.
請(qǐng)記住债朵,在使用擴(kuò)展時(shí),擴(kuò)展中的方法不能被子類覆蓋瀑凝,否則可能會(huì)使測(cè)試變得困難序芦。如果這種情況經(jīng)常發(fā)生,為了保持一致性粤咪,最好堅(jiān)持使用第一種方式谚中。否則使用第二種方式,會(huì)使代碼分離得更干凈寥枝。
Even when using method #2, add // MARK:
statements anyway for easier readability in Xcode's method/property/class/etc. list UI.
盡管使用第二種方式宪塔,也要添加// MARK:
語句,以便在Xcode的method/property/class列表界面中更容易閱讀囊拜。
3.7 Properties
3.7 屬性
-
3.7.1 If making a read-only, computed property, provide the getter without the
get {}
around it. -
3.7.1 如果要?jiǎng)?chuàng)建一個(gè)
read-only
計(jì)算屬性某筐,請(qǐng)?zhí)峁ゞetter但無需使用get {}
包住它。
var computedProperty: String {
if someBool {
return "I'm a mighty pirate!"
}
return "I'm selling these fine leather jackets."
}
3.7.2 When using
get {}
,set {}
,willSet
, anddidSet
, indent these blocks.3.7.2 使用
get {}
冠跷,set {}
南誊,willSet
和didSet
時(shí),注意縮進(jìn)代碼塊蜜托。3.7.3 Though you can create a custom name for the new or old value for
willSet
/didSet
andset
, use the standardnewValue
/oldValue
identifiers that are provided by default.3.7.3 盡管您可以為
willSet
/didSet
和set
的新值或舊值創(chuàng)建自定義名稱抄囚,但推薦使用默認(rèn)提供的標(biāo)準(zhǔn)名稱newValue
/oldValue
。
var storedProperty: String = "I'm selling these fine leather jackets." {
willSet {
print("will set to \(newValue)")
}
didSet {
print("did set from \(oldValue) to \(storedProperty)")
}
}
var computedProperty: String {
get {
if someBool {
return "I'm a mighty pirate!"
}
return storedProperty
}
set {
storedProperty = newValue
}
}
- 3.7.4 You can declare a singleton property as follows:
- 3.7.4 您可以用下面的方式聲明單例盗冷。
class PirateManager {
static let shared = PirateManager()
/* ... */
}
3.8 Closures
3.8 閉包
- 3.8.1 If the types of the parameters are obvious, it is OK to omit the type name, but being explicit is also OK. Sometimes readability is enhanced by adding clarifying detail and sometimes by taking repetitive parts away - use your best judgment and be consistent.
- 3.8.1 如果參數(shù)的類型明顯,可以省略類型名稱同廉,當(dāng)然顯式類型也可以仪糖。有時(shí)可以通過添加澄清細(xì)節(jié)來提高可讀性柑司,有時(shí)通過刪除重復(fù)的部分來提高可讀性——使用你的最佳判斷力并保持一致性。
// omitting the type
doSomethingWithClosure() { response in
print(response)
}
// explicit type
doSomethingWithClosure() { response: NSURLResponse in
print(response)
}
// using shorthand in a map statement
[1, 2, 3].flatMap { String($0) }
譯者注:閉包中參數(shù)如果能清楚地推測(cè)出類型锅劝,可以不用寫類型攒驰,否則還是把類型寫出來提高可讀性。
-
3.8.2 If specifying a closure as a type, you don’t need to wrap it in parentheses unless it is required (e.g. if the type is optional or the closure is within another closure). Always wrap the arguments in the closure in a set of parentheses - use
()
to indicate no arguments and useVoid
to indicate that nothing is returned. -
3.8.2 如果將
閉包
指定為類型故爵,不需要將其包裝在括號(hào)中玻粪,除非有必要(例如,如果類型是可選的或閉包在另一個(gè)閉包中)诬垂。始終將閉包中的參數(shù)包裹在一組括號(hào)中 - 使用()
表示沒有參數(shù)劲室,使用Void
表示不返回任何內(nèi)容。
let completionBlock: (Bool) -> Void = { (success) in
print("Success? \(success)")
}
let completionBlock: () -> Void = {
print("Completed!")
}
let completionBlock: (() -> Void)? = nil
3.8.3 Keep parameter names on same line as the opening brace for closures when possible without too much horizontal overflow (i.e. ensure lines are less than 160 characters).
3.8.3 在沒有太多水平溢出的情況下结窘,盡可能將參數(shù)名稱與閉包的左大括號(hào)保持在同一行(即確保行少于 160 個(gè)字符)很洋。
3.8.4 Use trailing closure syntax unless the meaning of the closure is not obvious without the parameter name (an example of this could be if a method has parameters for success and failure closures).
3.8.4 使用尾隨閉包語法,除非閉包的含義在沒有參數(shù)名稱的情況下不明顯(例如隧枫,如果方法具有成功和失敗閉包的參數(shù))喉磁。
// trailing closure
doSomething(1.0) { (parameter1) in
print("Parameter 1 is \(parameter1)")
}
// no trailing closure
doSomething(1.0, success: { (parameter1) in
print("Success with \(parameter1)")
}, failure: { (parameter1) in
print("Failure with \(parameter1)")
})
譯者注:其實(shí)Xcode中回車補(bǔ)全閉包參數(shù)時(shí)自動(dòng)轉(zhuǎn)化為尾隨閉包了,有必要的情況下可以自己手寫補(bǔ)全閉包官脓。
3.9 Arrays
3.9 數(shù)組
3.9.1 In general, avoid accessing an array directly with subscripts. When possible, use accessors such as
.first
or.last
, which are optional and won’t crash. Prefer using afor item in items
syntax when possible as opposed to something likefor i in 0 ..< items.count
. If you need to access an array subscript directly, make sure to do proper bounds checking. You can usefor (index, value) in items.enumerated()
to get both the index and the value.3.9.1 一般情況下协怒,避免使用下標(biāo)直接訪問數(shù)組。 如果可能卑笨,請(qǐng)使用諸如
.first
或.last
之類的訪問器孕暇,它們是可選的并且不會(huì)崩潰。 盡可能使用for item in items
語法湾趾,而不是像for i in 0 ..< items.count
這樣的語法芭商。 如果您需要直接訪問數(shù)組下標(biāo),請(qǐng)確保進(jìn)行適當(dāng)?shù)倪吔鐧z查搀缠。 您可以在items.enumerated()
中使用for (index, value)
來獲取索引和值铛楣。3.9.2 Never use the
+=
or+
operator to append/concatenate to arrays. Instead, use.append()
or.append(contentsOf:)
as these are far more performant (at least with respect to compilation) in Swift's current state. If you are declaring an array that is based on other arrays and want to keep it immutable, instead oflet myNewArray = arr1 + arr2
, uselet myNewArray = [arr1, arr2].joined()
.3.9.2 切勿使用
+=
或+
運(yùn)算符來追加/連接到數(shù)組。 相反艺普,使用.append()
或.append(contentsOf:)
因?yàn)樗鼈冊(cè)?Swift 當(dāng)前狀態(tài)下的性能要高得多(至少在編譯方面)簸州。 如果您要聲明一個(gè)基于其他數(shù)組的數(shù)組并希望使其保持不變,請(qǐng)使用let myNewArray = [arr1, arr2].joined()
而不是let myNewArray = arr1 + arr2
歧譬。
譯者注:這點(diǎn)我自己做的不好岸浑,
jioned()
真的很少用,相反數(shù)組+
真的很好用瑰步,要舍棄真的有點(diǎn)舍不得矢洲,今后會(huì)多了解兩者的使用場(chǎng)景。
3.10 Error Handling
3.10 錯(cuò)誤處理
Suppose a function myFunction
is supposed to return a String
, however, at some point it can run into an error. A common approach is to have this function return an optional String?
where we return nil
if something went wrong.
假設(shè)函數(shù)myFunction
應(yīng)該返回一個(gè)String
缩焦,但是读虏,在某些時(shí)候它可能會(huì)遇到錯(cuò)誤责静。 常見的做法是讓這個(gè)函數(shù)返回一個(gè)可選的 String?
,如果出現(xiàn)問題盖桥,我們就返回 nil
灾螃。
Example:
例子:
func readFile(named filename: String) -> String? {
guard let file = openFile(named: filename) else {
return nil
}
let fileContents = file.read()
file.close()
return fileContents
}
func printSomeFile() {
let filename = "somefile.txt"
guard let fileContents = readFile(named: filename) else {
print("Unable to open file \(filename).")
return
}
print(fileContents)
}
Instead, we should be using Swift's try
/catch
behavior when it is appropriate to know the reason for the failure.
如果我們希望了解失敗原因,可以使用Swift 的 try
/catch
揩徊。
You can use a struct
such as the following:
您可以使用struct
腰鬼,例如:
struct Error: Swift.Error {
public let file: StaticString
public let function: StaticString
public let line: UInt
public let message: String
public init(message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
self.file = file
self.function = function
self.line = line
self.message = message
}
}
Example usage:
用法示例:
func readFile(named filename: String) throws -> String {
guard let file = openFile(named: filename) else {
throw Error(message: "Unable to open file named \(filename).")
}
let fileContents = file.read()
file.close()
return fileContents
}
func printSomeFile() {
do {
let fileContents = try readFile(named: filename)
print(fileContents)
} catch {
print(error)
}
}
There are some exceptions in which it does make sense to use an optional as opposed to error handling. When the result should semantically potentially be nil
as opposed to something going wrong while retrieving the result, it makes sense to return an optional instead of using error handling.
在某些例外情況下,使用可選項(xiàng)確實(shí)比錯(cuò)誤處理更有意義塑荒。 當(dāng)結(jié)果在語義上 可能是 nil
而不是在檢索結(jié)果時(shí)出現(xiàn)錯(cuò)誤時(shí)熄赡,返回一個(gè)可選項(xiàng)比使用錯(cuò)誤處理更有意義。
In general, if a method can "fail", and the reason for the failure is not immediately obvious if using an optional return type, it probably makes sense for the method to throw an error.
一般來說袜炕,如果一個(gè)方法可能會(huì)失敗
本谜,并且如果使用可選返回類型,但失敗的原因不是很明顯偎窘,那么該方法拋出錯(cuò)誤可能會(huì)更有意義乌助。
3.11 Using guard
Statements
3.11 使用guard
語句
-
3.11.1 In general, we prefer to use an "early return" strategy where applicable as opposed to nesting code in
if
statements. Usingguard
statements for this use-case is often helpful and can improve the readability of the code. -
3.11.1 一般來說,我們更喜歡在適用的情況下使用“提前返回”策略陌知,而不是在
if
語句中嵌套代碼他托。 在這個(gè)用例中使用guard
語句通常很有幫助,并且可以提高代碼的可讀性仆葡。
// PREFERRED
func eatDoughnut(at index: Int) {
guard index >= 0 && index < doughnuts.count else {
// return early because the index is out of bounds
return
}
let doughnut = doughnuts[index]
eat(doughnut)
}
// NOT PREFERRED
func eatDoughnut(at index: Int) {
if index >= 0 && index < doughnuts.count {
let doughnut = doughnuts[index]
eat(doughnut)
}
}
-
3.11.2 When unwrapping optionals, prefer
guard
statements as opposed toif
statements to decrease the amount of nested indentation in your code. -
3.11.2 展開可選項(xiàng)時(shí)赏参,更喜歡使用
guard
語句而不是if
語句,以減少代碼中嵌套縮進(jìn)的數(shù)量沿盅。
// PREFERRED
guard let monkeyIsland = monkeyIsland else {
return
}
bookVacation(on: monkeyIsland)
bragAboutVacation(at: monkeyIsland)
// NOT PREFERRED
if let monkeyIsland = monkeyIsland {
bookVacation(on: monkeyIsland)
bragAboutVacation(at: monkeyIsland)
}
// EVEN LESS PREFERRED
if monkeyIsland == nil {
return
}
bookVacation(on: monkeyIsland!)
bragAboutVacation(at: monkeyIsland!)
-
3.11.3 When deciding between using an
if
statement or aguard
statement when unwrapping optionals is not involved, the most important thing to keep in mind is the readability of the code. There are many possible cases here, such as depending on two different booleans, a complicated logical statement involving multiple comparisons, etc., so in general, use your best judgement to write code that is readable and consistent. If you are unsure whetherguard
orif
is more readable or they seem equally readable, prefer usingguard
. -
3.11.3 在不涉及展開可選項(xiàng)的情況下決定使用
if
語句還是guard
語句時(shí)把篓,要記住的最重要的事情是代碼的可讀性。 這里有很多可能的情況腰涧,比如依賴于兩個(gè)不同的布爾值韧掩,一個(gè)復(fù)雜的邏輯語句涉及多重比較等等,所以一般來說窖铡,使用你最好的判斷來編寫可讀且一致的代碼疗锐。 如果您不確定guard
或if
是否更具可讀性,或者它們看起來同樣可讀费彼,請(qǐng)首選使用guard
滑臊。
// an `if` statement is readable here
if operationFailed {
return
}
// a `guard` statement is readable here
guard isSuccessful else {
return
}
// double negative logic like this can get hard to read - i.e. don't do this
guard !operationFailed else {
return
}
-
3.11.4 If choosing between two different states, it makes more sense to use an
if
statement as opposed to aguard
statement. -
3.11.4 如果在兩個(gè)不同的狀態(tài)之間進(jìn)行選擇,使用
if
語句比使用guard
語句更有意義箍铲。
// PREFERRED
if isFriendly {
print("Hello, nice to meet you!")
} else {
print("You have the manners of a beggar.")
}
// NOT PREFERRED
guard isFriendly else {
print("You have the manners of a beggar.")
return
}
print("Hello, nice to meet you!")
-
3.11.5 You should also use
guard
only if a failure should result in exiting the current context. Below is an example in which it makes more sense to use twoif
statements instead of using twoguard
s - we have two unrelated conditions that should not block one another. -
3.11.5 僅當(dāng)失敗導(dǎo)致退出當(dāng)前上下文時(shí)雇卷,您才可以使用
guard
。 下面是一個(gè)示例,這里使用兩個(gè)if
語句而不應(yīng)該使用兩個(gè)guard
語句——這里是兩個(gè)不應(yīng)該相互阻塞的不相關(guān)條件关划。
if let monkeyIsland = monkeyIsland {
bookVacation(onIsland: monkeyIsland)
}
if let woodchuck = woodchuck, canChuckWood(woodchuck) {
woodchuck.chuckWood()
}
-
3.11.6 Often, we can run into a situation in which we need to unwrap multiple optionals using
guard
statements. In general, combine unwraps into a singleguard
statement if handling the failure of each unwrap is identical (e.g. just areturn
,break
,continue
,throw
, or some other@noescape
). -
3.11.6 通常膘融,我們會(huì)遇到需要使用
guard
語句解開多個(gè)可選項(xiàng)的情況。 通常祭玉,如果處理每個(gè)解包的失敗是相同的(例如,只是一個(gè)return
春畔、break
脱货、continue
、throw
或其他一些@noescape
)律姨,則將多個(gè)解包語句合并為一個(gè)guard
語句振峻。
// combined because we just return
guard let thingOne = thingOne,
let thingTwo = thingTwo,
let thingThree = thingThree else {
return
}
// separate statements because we handle a specific error in each case
guard let thingOne = thingOne else {
throw Error(message: "Unwrapping thingOne failed.")
}
guard let thingTwo = thingTwo else {
throw Error(message: "Unwrapping thingTwo failed.")
}
guard let thingThree = thingThree else {
throw Error(message: "Unwrapping thingThree failed.")
}
-
3.11.7 Don’t use one-liners for
guard
statements. -
3.11.7 不要在
guard
語句中使用單行。
// PREFERRED
guard let thingOne = thingOne else {
return
}
// NOT PREFERRED
guard let thingOne = thingOne else { return }
4. Documentation/Comments
4.1 Documentation
If a function is more complicated than a simple O(1) operation, you should generally consider adding a doc comment for the function since there could be some information that the method signature does not make immediately obvious. If there are any quirks to the way that something was implemented, whether technically interesting, tricky, not obvious, etc., this should be documented. Documentation should be added for complex classes/structs/enums/protocols and properties. All public
functions/classes/properties/constants/structs/enums/protocols/etc. should be documented as well (provided, again, that their signature/name does not make their meaning/functionality immediately obvious).
如果函數(shù)比簡(jiǎn)單的 O(1) 操作更復(fù)雜择份,您通常應(yīng)該考慮為函數(shù)添加 doc 注釋扣孟,因?yàn)榭赡艽嬖谝恍┓椒ê灻ǚ椒蛥?shù)列表)并不是那么一目了然。 如果某些方法的實(shí)現(xiàn)方式有任何不易理解的代碼荣赶,都應(yīng)該記錄下來凤价。 應(yīng)該為復(fù)雜的類/結(jié)構(gòu)/枚舉/協(xié)議和屬性添加文檔。 所有public
函數(shù)/類/屬性/常量/結(jié)構(gòu)/枚舉/協(xié)議/等拔创。 也應(yīng)該記錄在案(同樣前提是它們的簽名/名稱不能使功能一目了然)利诺。
After writing a doc comment, you should option click the function/property/class/etc. to make sure that everything is formatted correctly.
編寫文檔注釋后,您應(yīng)該選擇單擊函數(shù)/屬性/類/等剩燥,以確保所有內(nèi)容格式正確慢逾。
Be sure to check out the full set of features available in Swift's comment markup described in Apple's Documentation.
請(qǐng)務(wù)必查看 Swift 注釋標(biāo)記中可用的全套功能Apple 文檔中的描述。
Guidelines:
準(zhǔn)則:
4.1.1 160 character column limit (like the rest of the code).
4.1.1 160 個(gè)字符的列限制(與代碼的其余部分一樣)灭红。
4.1.2 Even if the doc comment takes up one line, use block (
/** */
).4.1.2 即使文檔注釋占一行侣滩,也要使用block (
/** */
)。4.1.3 Do not prefix each additional line with a
*
.4.1.3 不要在每個(gè)附加行前加上
*
变擒。4.1.4 Use the new
- parameter
syntax as opposed to the old:param:
syntax (make sure to use lower caseparameter
and notParameter
). Option-click on a method you wrote to make sure the quick help looks correct.4.1.4 使用新的
- parameter
語法而不是舊的:param:
語法(確保使用小寫的parameter
而不是Parameter
)君珠。 按住 Option 鍵單擊您編寫的方法以確保快速幫助看起來正確赁项。
class Human {
/**
This method feeds a certain food to a person.
- parameter food: The food you want to be eaten.
- parameter person: The person who should eat the food.
- returns: True if the food was eaten by the person; false otherwise.
*/
func feed(_ food: Food, to person: Human) -> Bool {
// ...
}
}
4.1.5 If you’re going to be documenting the parameters/returns/throws of a method, document all of them, even if some of the documentation ends up being somewhat repetitive (this is preferable to having the documentation look incomplete). Sometimes, if only a single parameter warrants documentation, it might be better to just mention it in the description instead.
4.1.5 如果您要記錄方法的參數(shù)/返回/拋出異常葛躏,請(qǐng)記錄所有這些,即使某些文檔最終有些啰嗦(這比不完整的文檔更好)悠菜。有時(shí)舰攒,如果只有一個(gè)參數(shù)需要記錄,最好在描述中提及它悔醋。
4.1.6 For complicated classes, describe the usage of the class with some potential examples as seems appropriate. Remember that markdown syntax is valid in Swift's comment docs. Newlines, lists, etc. are therefore appropriate.
4.1.6 對(duì)于復(fù)雜的類摩窃,用一些看起來合適的潛在例子來描述類的用法。 請(qǐng)記住 Markdown 語法在 Swift 的注釋文檔中是有效的。 因此猾愿,換行符鹦聪、列表等是合適的。
/**
## Feature Support
This class does some awesome things. It supports:
- Feature 1
- Feature 2
- Feature 3
## Examples
Here is an example use case indented by four spaces because that indicates a
code block:
let myAwesomeThing = MyAwesomeClass()
myAwesomeThing.makeMoney()
## Warnings
There are some things you should be careful of:
1. Thing one
2. Thing two
3. Thing three
*/
class MyAwesomeClass {
/* ... */
}
- 4.1.7 When mentioning code, use code ticks - `
/**
This does something with a `UIViewController`, perchance.
- warning: Make sure that `someValue` is `true` before running this function.
*/
func myFunction() {
/* ... */
}
- 4.1.8 When writing doc comments, prefer brevity where possible.
- 4.1.8 在編寫文檔注釋時(shí)蒂秘,盡可能簡(jiǎn)潔泽本。
4.2 Other Commenting Guidelines
4.2 其他注釋指南
4.2.1 Always leave a space after
//
.4.2.1 始終在
//
后面加上一個(gè)空格.4.2.2 Always leave comments on their own line.
4.2.2 注釋必須另起一行。
4.2.3 When using
// MARK: - whatever
, leave a newline after the comment.4.2.3 使用
// MARK: - whatever
時(shí)姻僧,在注釋后換行规丽。
class Pirate {
// MARK: - instance properties
private let pirateName: String
// MARK: - initialization
init() {
/* ... */
}
}