Swift编码规范

A guide to our Swift style and conventions.

This is an attempt to encourage patterns that accomplish the following goals (in
rough priority order):

  1. Increased rigor, and decreased likelihood of programmer error
  2. Increased clarity of intent
  3. Reduced verbosity
  4. Fewer debates about aesthetics

If you have suggestions, please see our contribution guidelines,
then open a pull request. :zap:


Whitespace

  • Tabs, not spaces.
  • End files with a newline.
  • Make liberal use of vertical whitespace to divide code into logical chunks.
  • Don’t leave trailing whitespace.
    • Not even leading indentation on blank lines.

Prefer let-bindings over var-bindings wherever possible

Use let foo = … over var foo = … wherever possible (and when in doubt). Only use var if you absolutely have to (i.e. you know that the value might change, e.g. when using the weak storage modifier).

Rationale: The intent and meaning of both keywords are clear, but let-by-default results in safer and clearer code.

A let-binding guarantees and clearly signals to the programmer that its value will never change. Subsequent code can thus make stronger assumptions about its usage.

It becomes easier to reason about code. Had you used var while still making the assumption that the value never changed, you would have to manually check that.

Accordingly, whenever you see a var identifier being used, assume that it will change and ask yourself why.

Return and break early

When you have to meet certain criteria to continue execution, try to exit early. So, instead of this:

if n.isNumber {
    // Use n here
} else {
    return
}

use this:

guard n.isNumber else {
    return
}
// Use n here

You can also do it with if statement, but using guard is preferred, because guard statement without return, break or continue produces a compile-time error, so exit is guaranteed.

Avoid Using Force-Unwrapping of Optionals

If you have an identifier foo of type FooType? or FooType!, don't force-unwrap it to get to the underlying value (foo!) if possible.

Instead, prefer this:

if let foo = foo {
    // Use unwrapped `foo` value in here
} else {
    // If appropriate, handle the case where the optional is nil
}

Alternatively, you might want to use Swift's Optional Chaining in some of these cases, such as:

// Call the function if `foo` is not nil. If `foo` is nil, ignore we ever tried to make the call
foo?.callSomethingIfFooIsNotNil()

Rationale: Explicit if let-binding of optionals results in safer code. Force unwrapping is more prone to lead to runtime crashes.

Avoid Using Implicitly Unwrapped Optionals

Where possible, use let foo: FooType? instead of let foo: FooType! if foo may be nil (Note that in general, ? can be used instead of !).

Rationale: Explicit optionals result in safer code. Implicitly unwrapped optionals have the potential of crashing at runtime.

Prefer implicit getters on read-only properties and subscripts

When possible, omit the get keyword on read-only computed properties and
read-only subscripts.

So, write these:

var myGreatProperty: Int {
    return 4
}

subscript(index: Int) -> T {
    return objects[index]
}

… not these:

var myGreatProperty: Int {
    get {
        return 4
    }
}

subscript(index: Int) -> T {
    get {
        return objects[index]
    }
}

Rationale: The intent and meaning of the first version are clear, and results in less code.

Always specify access control explicitly for top-level definitions

Top-level functions, types, and variables should always have explicit access control specifiers:

public var whoopsGlobalState: Int
internal struct TheFez {}
private func doTheThings(things: [Thing]) {}

However, definitions within those can leave access control implicit, where appropriate:

internal struct TheFez {
    var owner: Person = Joshaber()
}

Rationale: It's rarely appropriate for top-level definitions to be specifically internal, and being explicit ensures that careful thought goes into that decision. Within a definition, reusing the same access control specifier is just duplicative, and the default is usually reasonable.

When specifying a type, always associate the colon with the identifier

When specifying the type of an identifier, always put the colon immediately
after the identifier, followed by a space and then the type name.

class SmallBatchSustainableFairtrade: Coffee { ... }

let timeToCoffee: NSTimeInterval = 2

func makeCoffee(type: CoffeeType) -> Coffee { ... }

Rationale: The type specifier is saying something about the identifier so
it should be positioned with it.

Also, when specifying the type of a dictionary, always put the colon immediately
after the key type, followed by a space and then the value type.

let capitals: [Country: City] = [sweden: stockholm]

Only explicitly refer to self when required

When accessing properties or methods on self, leave the reference to self implicit by default:

private class History {
    var events: [Event]

    func rewrite() {
        events = []
    }
}

Only include the explicit keyword when required by the language—for example, in a closure, or when parameter names conflict:

extension History {
    init(events: [Event]) {
        self.events = events
    }

    var whenVictorious: () -> () {
        return {
            self.rewrite()
        }
    }
}

Rationale: This makes the capturing semantics of self stand out more in closures, and avoids verbosity elsewhere.

Prefer structs over classes

Unless you require functionality that can only be provided by a class (like identity or deinitializers), implement a struct instead.

Note that inheritance is (by itself) usually not a good reason to use classes, because polymorphism can be provided by protocols, and implementation reuse can be provided through composition.

For example, this class hierarchy:

class Vehicle {
    let numberOfWheels: Int

    init(numberOfWheels: Int) {
        self.numberOfWheels = numberOfWheels
    }

    func maximumTotalTirePressure(pressurePerWheel: Float) -> Float {
        return pressurePerWheel * Float(numberOfWheels)
    }
}

class Bicycle: Vehicle {
    init() {
        super.init(numberOfWheels: 2)
    }
}

class Car: Vehicle {
    init() {
        super.init(numberOfWheels: 4)
    }
}

could be refactored into these definitions:

protocol Vehicle {
    var numberOfWheels: Int { get }
}

func maximumTotalTirePressure(vehicle: Vehicle, pressurePerWheel: Float) -> Float {
    return pressurePerWheel * Float(vehicle.numberOfWheels)
}

struct Bicycle: Vehicle {
    let numberOfWheels = 2
}

struct Car: Vehicle {
    let numberOfWheels = 4
}

Rationale: Value types are simpler, easier to reason about, and behave as expected with the let keyword.

Make classes final by default

Classes should start as final, and only be changed to allow subclassing if a valid need for inheritance has been identified. Even in that case, as many definitions as possible within the class should be final as well, following the same rules.

Rationale: Composition is usually preferable to inheritance, and opting in to inheritance hopefully means that more thought will be put into the decision.

Omit type parameters where possible

Methods of parameterized types can omit type parameters on the receiving type when they’re identical to the receiver’s. For example:

struct Composite<T> {
    …
    func compose(other: Composite<T>) -> Composite<T> {
        return Composite<T>(self, other)
    }
}

could be rendered as:

struct Composite<T> {
    …
    func compose(other: Composite) -> Composite {
        return Composite(self, other)
    }
}

Rationale: Omitting redundant type parameters clarifies the intent, and makes it obvious by contrast when the returned type takes different type parameters.

Use whitespace around operator definitions

Use whitespace around operators when defining them. Instead of:

func <|(lhs: Int, rhs: Int) -> Int
func <|<<A>(lhs: A, rhs: A) -> A

write:

func <| (lhs: Int, rhs: Int) -> Int
func <|< <A>(lhs: A, rhs: A) -> A

Rationale: Operators consist of punctuation characters, which can make them difficult to read when immediately followed by the punctuation for a type or value parameter list. Adding whitespace separates the two more clearly.

Translations

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

推荐阅读更多精彩内容