學(xué)Swift速記

Property(屬性)

屬性將值與特定的類、結(jié)構(gòu)體或枚舉聯(lián)系起來。Stored屬性將常量或變量存儲作為實(shí)例的一部分,而computed屬性計(jì)算而不是存儲值。Computed屬性是類、結(jié)構(gòu)體或枚舉提供的,Stored屬性只有類或結(jié)構(gòu)體提供 。

Stored或Computed屬性通常與特定類型的實(shí)例相關(guān)聯(lián)。但是,屬性也可以與類型本身相關(guān)聯(lián)。這樣的屬性稱為type property。

最簡單的情況下,stored property就是作為一個類、結(jié)構(gòu)體的實(shí)例的一部分而存儲。

必須將一個lazy屬性聲明為var,因?yàn)樗闹悼赡茉趯?shí)例初始化完成后才能獲得。constant要求在初始化完成時就已經(jīng)有值,因此不能是lazy的。

Lazy屬性在初始化依靠于外部因素時比較有用,如果一個屬性的值要復(fù)雜計(jì)算開銷很大時,使用lazy也是很合適的

注意,lazy屬性的初始化過程不是線程安全的!

Objective C中提供了兩個方式來存儲值和引用,除了屬性,你還可以使用instance variable as a backing store。
Swift將這些概念統(tǒng)一到了一個單獨(dú)的屬性聲明中。一個Swift屬性沒有對應(yīng)的instance variable,屬性的backing store不能直接被訪問。這種方式避免了值在不同上下文如何訪問的困惑。屬性的所有信息,都作為類型定義的一部分在同一個位置定義。

除了Stored屬性,類、結(jié)構(gòu)體和枚舉還提供computed屬性,它實(shí)際上不存儲值,只是提供一個getter方法(setter方法可選)來間接獲取和設(shè)置其它屬性的值。

如果將一個有observer的屬性作為函數(shù)的in-out參數(shù)傳遞,那么willSetdidSet總會被調(diào)用。因?yàn)閕n-out參數(shù)的copy-in、copy-out內(nèi)存模型使得函數(shù)在結(jié)束時總會回寫屬性的值。

關(guān)于全局和局部變量

全局的變量或常量總是惰性計(jì)算的,類似于惰性存儲的屬性,不同的是全局的常量和變量不需要聲明為lazy
局部常量和變量永遠(yuǎn)不會惰性計(jì)算

Type Property

Type屬性必須給初值,因?yàn)闆]有initializer來初始化它。Stored type屬性第一次訪問是惰性的,它們能保證只初始化一次。

繼承

Swift的類沒有繼承一個通用的基類。如果沒有給定義的類指定superclass的話,那么你定義的這個類就是自己的基類。

Overriding

  1. 重寫Property Getters 和 Setters
    可以通過提供自定義的getter或setter重寫任何繼承的屬性,不管這個屬性是stored還是computed屬性。子類并不是知道繼承屬性是stored還是computed,它只知道屬性的類型和名字。
    可以通過提供getter和setter來重寫只讀屬性為可讀寫的屬性。但是,不能將可讀寫的屬性重寫為只讀的。

如果你提供了屬性setter方法的重寫,你也必須提供getter方法。如果你不想在getter方法內(nèi)修改繼承屬性的值,只需要在方法內(nèi)簡單地返回super.someProperty。

  1. 重寫Property Observers

不能給繼承的常量stored屬性或者只讀computed屬性添加observers。這些屬性的值是不能修改的
同時,你不能同時重寫一個屬性的setter和observer。如果你想觀測屬性值的改變,同時也想提供自定義的setter方法,你只需要在setter方法中觀測值的改變就好了。

3.重寫方法

禁止Overrides

可以通過設(shè)置method、property或subscript為final來禁止重寫??梢詫lass標(biāo)記為final,這樣任何類都不能繼承這個類。

Structure也支持class的很多行為,比如methods和initializer,最重要的一點(diǎn)區(qū)別就是structure傳遞的時候總是被拷貝的,但是class是通過引用。Structure適合一些輕量級的不需要繼承和類型轉(zhuǎn)換的數(shù)據(jù)結(jié)構(gòu)。

Protocol

A protocol defines a blueprint of methods,properties and other requirements that suit a particular task or piece of functionality.

Protocol可以被class、structure和enumeration實(shí)現(xiàn)

protocol ExampleProtocol{
    var simpleDescription:String {get}
    func adjust()
}

{get}表示這個屬性是只讀的,不能被修改

Protocol are first-class types,which means they can be treated like other named types.即協(xié)議也跟普通的數(shù)據(jù)類型一樣被對待。比如,也可以創(chuàng)建一個協(xié)議的數(shù)組等。

ARC

類間相互引用的三種情況

  1. 兩個屬性都可以是nil,互相引用時可能會導(dǎo)致強(qiáng)引用循環(huán)。這個場景最好用weak reference進(jìn)行解決(人和住宅的例子)
  2. 兩個屬性,一個可以是nil,另一個不能為nil,這種場景下最好用unowned reference(人和信用卡的例子)
  3. 兩個屬性都必須不為nil時。這種情況下,一個類使用unowned reference,另一個使用implicit unwrapped optional。

Initialization

Classes和Structures必須在實(shí)例創(chuàng)建時給所有的stored屬性賦初始值??梢栽趇nitializer中賦值,也可以在聲明屬性時設(shè)置默認(rèn)值。

當(dāng)設(shè)置stored屬性默認(rèn)值或在initializer中初始化時,屬性的observer不會被調(diào)用

Initializer delegation:Initializer可以調(diào)用其他的initializer來完成初始化過程,避免重復(fù)的代碼

Value types(structures和enumerations)不支持繼承,所以initialize delegate只是調(diào)用自己定義的其他initializer。

如果你想同時用默認(rèn)的default initializer和memberwise initializer、自定義的initializer來初始化,那么可以將自定義的initializer放到extension中。

Designated Initializer And Convenience Initializer

Designated Initializer是類的主要初始化方法,它完全初始化所有的屬性,調(diào)用superclass的initializer來傳遞superclass chain。每個類都至少要有一個。
Convenience Initializer是次要的,支持性的initializer??梢远x它來調(diào)用designated initializer(設(shè)置某些屬性為默認(rèn)值)。

Initializer Delegation For Class Types

規(guī)則1
Designated initializer必須調(diào)用 immediate superclass的designated initializer
規(guī)則2
Convenience initializer必須調(diào)用同一個類的initializer
規(guī)則3
Convenience initializer必須調(diào)用designated initializer

Initializer.png

簡單的方式,

  • Designated initializer必須delegate up
  • Convenience initializer必須delegate cross

Two-Phase Initialization

Swift的編譯器為了確保two-phase initialization正確執(zhí)行而進(jìn)行一些安全性檢查。
Safety Check1
Designated initializer必須確保在initializer傳遞到superclass前所有的屬性都已經(jīng)初始化了。
只要所有的stored屬性都初始化,就認(rèn)為這個對象的內(nèi)存已經(jīng)完全初始化了。所以,designated initializer必須確保在傳遞初始化鏈前所有的屬性都初始化。
Safety Check2
Designated initializer必須在給繼承屬性賦值前調(diào)用superclass initializer,如果不這么做,賦值會被superclass的initializer覆蓋。
Safety Check3
Convenience Initializer必須在給任何屬性賦值前調(diào)用其他的initializer。如果不這么做,賦值會被designated initializer覆蓋。
Safety Check4
Initializer不能調(diào)用任何實(shí)例方法,讀取任何實(shí)例屬性,或引用self,直到initializer的first phase完成。

Swift初始化主要分兩步,第一步給每個stored屬性賦一個初始值,一旦每個屬性的初始值確定,第二個步驟就開始了,在實(shí)例被使用之前,每個類都有機(jī)會自定義stored屬性。

Swift的兩步初始化過程與OC的類似。不同點(diǎn)在于第一步時,OC給每個屬性賦zero或null。Swift的初始化更靈活,可以有自定義的初始值。

Phase 1

  • Initializer被調(diào)用
  • 分配內(nèi)存,但內(nèi)存還未初始化
  • designated intializer確認(rèn)所有的stored屬性都有值,這些屬性的內(nèi)存開始初始化
  • designated initializer交給superclass的initializer給它自己的屬性進(jìn)行同樣的任務(wù)
  • 繼承鏈進(jìn)行傳遞
  • 繼承鏈到頂之后,鏈上的最后一個類確保所有的stored屬性都有值,實(shí)例的內(nèi)存被認(rèn)為完全初始化了

Phase 2

  • 從繼承鏈往回遍歷,每個designated initializer都有機(jī)會自定義實(shí)例。initializer可以訪問self,修改屬性,調(diào)用實(shí)例方法等等
  • 最后,任何的convenience initializer可以選擇自定義實(shí)例

Swift的subclass默認(rèn)是不繼承superclass的initializer的。但是特定情況下superclass的initializer會被繼承。假設(shè)subclass引入的新的屬性都有默認(rèn)值,那么會應(yīng)用下面規(guī)則:
規(guī)則1
如果subclass沒有定義任何的initializer,它自動繼承superclass所有的designated initializer。
規(guī)則2
如果subclass提供了所有的designated initializer的實(shí)現(xiàn)(通過規(guī)則1或自定義),那么它自動繼承所有的convenience initializer。(以convenience的方式也可以)

不能定義相同參數(shù)和名稱的failable、nonfailable initializer

failable initializer創(chuàng)建一個optional值。

如果使用closure來初始化屬性,牢記在closure執(zhí)行完畢之前其余的實(shí)例都沒有被初始化。這意味著在closure中不能訪問其他任意屬性,即使它有默認(rèn)值。也不能使用self屬性,或調(diào)用實(shí)例方法。

Closure

Global和nested function其實(shí)是特殊形式的closure。Closure有三種形式:

  • Global function,有名稱,不捕獲任何值
  • Nested function,有名稱,捕獲包含它的function內(nèi)的值
  • Closure,沒名稱,捕獲上下文的值

Trailing Closure

如果closure作為function的最后一個參數(shù),并且closure定義非常長,那么可以使用trailing closure的技巧,將closure定義寫在函數(shù)的外面。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function定義
}
 
// here's how you call this function without using a trailing closure:
 
func someFunctionThatTakesAClosure({
    // closure定義
})
 
// here's how you call this function with a trailing closure instead:
 
func someFunctionThatTakesAClosure() {
    // trailing closure's 定義
}

Nonescaping Closures

當(dāng)closure作為function的參數(shù),但是在function返回之后才被調(diào)用,就說這個closure escape a function。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

##Autoclosures
Autoclose是一個包含表達(dá)式、作為函數(shù)參數(shù)的的closure。它不包含任何參數(shù),當(dāng)調(diào)用時,它返回表達(dá)式的值。Autoclosure能延遲evaluation。
//Autoclosure,此時closure不執(zhí)行
let customerProvider = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)
// Prints "5"

//此時closure被真正調(diào)用 
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
  • AnyObject可以表示任意類的實(shí)例
  • Any可以表示任何類型的實(shí)例,包括函數(shù)類型

Extensions

Extensions可以給現(xiàn)有的class、structure、enumeration或protocol添加新的功能。與OC中的category類似,但是沒有名字。
Extensions可以:

  • 添加computed實(shí)例屬性和類型屬性
  • 定義實(shí)例方法和類方法
  • 提供新的initializer
  • 定義subscript
  • 定義和使用新的nexted types
  • 讓現(xiàn)有類型服從某一協(xié)議
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容