Swift Study

參考資源
《swifter》
https://github.com/iOS-Swift-Developers/Swift

閉包逃逸
swift3中,閉包默認是非逃逸的。如果一個函數(shù)參數(shù)可能導致引用循環(huán),那么它需要被顯示的標記出來。
沒有逃逸的閉包的作用域是不會超過函數(shù)本身的,
weakSelf unowned
如果能夠確定訪問時不會被釋放的話,盡量用unowned,如果存在被釋放的可能性的話,就用weak。
array 的高級函數(shù) filter map

閉包捕獲值
閉包和函數(shù)是引用類型
常量和變量
如果定義的時候初始化一個變量,可以不用寫數(shù)據(jù)類型,會根據(jù)初始化的值自動推斷出變量的類型(其他語言是沒有類型推斷的)
@objc 是為了 swift 和 oc 之間的交互,在使用 optional

泛型函數(shù)
如果函數(shù)的泛型列表只有一個 T ,每個節(jié)點類型的參數(shù)必須是一樣的。
可以對節(jié)點進行一些限制,比如要求泛類型遵守某些協(xié)議。
有時候節(jié)點中的泛型需要更多的限制,需要使用 where 語句來補充約束條件。
泛型協(xié)議
關鍵字
associaetdType
self :適用于比較這類方法,其必須傳入一個相同類型的參數(shù)才有意義。

方法
實例方法和函數(shù)的區(qū)別:函數(shù)默認沒有外部參數(shù)
在類方法中沒有 self
mutating 方法 :值類型(結構體和枚舉)默認方法是不可以修改屬性的,加 mutating 關鍵字,可以變?yōu)楦淖兎椒ā?br> 類方法: static 關鍵字(結構體/枚舉),class(類),不存在class

封裝
把隱藏屬性、方法和方法實現(xiàn)細節(jié)的過程稱為封裝
open:任何時候都能被繼承,被訪問
public 從外部模塊和本模塊都可以訪問.在模塊外不能被繼承,只能被訪問
internal:默認設置, 只有本模塊能訪問
private:只在當前類中使用
fileprivate:
多態(tài):用父類的類型調(diào)用子類的方法
嵌套類型:在一個類型中嵌套定義另一個類型

構造方法:
用于初始化屬性
隱式調(diào)用
如果所有的存儲屬性都有默認值,可以不提供構造方法
如果存儲屬性可以提供缺省值,盡量提供。簡化代碼
常量存儲屬性只允許通過缺省值和構造方法中被修改
可選值存儲屬性可以不在構造方法中初始化
結構體默認提供一個構造器
構造方法之間的相互調(diào)用稱為構造器代理
通過閉包或全局函數(shù)設置存儲屬性的缺省值
懶加載是用到才執(zhí)行,閉包賦值是初始化時就會執(zhí)行
被 convenience 關鍵字修飾的構造方法稱之為便利構造器。一定是通過調(diào)用其他構造方法來初始化,一定要出現(xiàn)self.init
派生類的構造方法 :
默認情況下構造方法不能被繼承
基類的存儲屬性只能通過基類的構造方法初始化
初始化存儲屬性必須先初始化當前類再初始化父類
便利構造器必須調(diào)用同類中的其他構造器(指定或者便利)
調(diào)用子類構造器一定能夠初始化所有屬性
只有在調(diào)用完父類指定構造器之后才能訪問父類屬性
確保當前類和父類所有存儲屬性都被初始化
重寫父類方法,加上 override 關鍵字,
便利構造方法不存在重寫
子類的便利構造方法不能直接訪問父類的方法,所以不存在重寫的概念。
只要在構造方法的前面加上一個required 關鍵字, 那么所有的子類只要定義了構造方法都必須實現(xiàn)該構造方法

函數(shù)參數(shù)
內(nèi)部函數(shù)
外部函數(shù)
第二個參數(shù)默認既是外部參數(shù)又是內(nèi)部參數(shù)
可以在定義函數(shù)的時候給某個參數(shù)默認值,當外部調(diào)用沒有傳遞該參數(shù)時自動使用默認值
默認情況下 Swift 中的參數(shù)都是常量參數(shù),如果需要在函數(shù)中改變參數(shù)的值,需要添加 var 關鍵字
可以將 var 換成 inout,這會傳遞參數(shù)本身而不是參數(shù)的值

函數(shù)類型
函數(shù)類型是由函數(shù)參數(shù)類型和返回值類型組成的。
可以利用函數(shù)類型定義函數(shù)變量和常量
函數(shù)類型可以作為函數(shù)的參數(shù)
函數(shù)類型可以作為函數(shù)的返回值

基本數(shù)據(jù)類型
整形
浮點型
長:Int8 Int 16 Int32 Int64

有符號:UInt
無符號:比有符號的取值范圍更大。
swift 是類型安全的語言,取值錯誤的時候會直接報錯。
oc中可以隱式類型轉化,而在swift 中是不可以的

繼承
新的類能夠繼承已有類的屬性和方法,并且擴展新的能力
優(yōu)點:代碼重用
缺點:增加代碼耦合性。
super 關鍵字
重寫屬性:無論是存儲屬性還是計算屬性,都只能重寫為計算屬性
屬性觀察器:willSet didSet
利用 final 關鍵字防止重寫
可以修飾屬性、方法和類
修飾的屬性和方法不能被重寫
修飾的類不能被繼承

結構體
用于封裝相同和不同類型數(shù)據(jù)的,swift 中結構體是一類類型,可以定義屬性和方法,甚至構造方法和析構方法。
如果結構體的屬性有默認值,可以使用()構造一個結構體
結構體有一個默認的逐一構造器,用于在初始化時給所有屬性賦值。
如果沒有,必須使用逐一構造器實例化
Swift 中結構體和其他面向對象語言一樣都有構造函數(shù),而 OC 沒有的
結構體中的成員方法必須使用某個實例調(diào)用
結構體是值類型
賦值有兩種情況
1.指向同一塊存儲空間
2.內(nèi)容相同的兩個不同實例
結構體是值類型,結構體間的賦值就是拷貝一份數(shù)據(jù),還是不同的實例

可選類型的本質其實是一個枚舉
None
Some
格式:Optional<類型>
由于可選類型在 Swift 中隨處可見,所以系統(tǒng)做了這個語法糖,在類型后面加 ?
基本類型變量在使用之前必須初始化,否則報錯
目的:安全,不管什么時候方法都是有意義的;
可選類型安全嗎?安全,可以使用可選綁定判斷后再使用。
Swift 的發(fā)明者出于安全的考量,讓我們在使用基本類型的時候不必考慮是否有值
Swift 中的可選類型變量更貼近于 oc 中的普通變量
可選鏈:通過可選類型變量來調(diào)用相應的屬性和方法
1.強制解包。非常危險
2.通過可選綁定,代碼繁瑣,但是簡單
3.通過可選鏈,如果 ?前面變量沒有值,那么整個可選鏈會無效
可選鏈的返回值會自動包裝成可選值
可選鏈調(diào)用下標索引,必須實現(xiàn) subscript 方法,訪問,實現(xiàn)get;賦值,實現(xiàn)set方法
判斷賦值操作是否成功。判斷返回值()? 或 Void? 都是代表成功

擴展
不需要知道目標源碼,就可以給一個現(xiàn)存類、枚舉、結構添加屬性或者方法
限制條件:
1.不能添加已經(jīng)存在的方法或屬性
2.添加的屬性不能是存儲屬性,只能是計算屬性
格式:extension 某個先有類型 {}
擴展整數(shù)類型
擴展下標:


Swift 中結構體和類非常相似,但又有不同之處
類是具有相同屬性和方法的抽象
類沒有逐一構造器
類是引用類型,結構體是值類型
恒等運算符。用于判斷是否是同一個實例,也就是是否指向同一存儲空間

類型轉換
不允許隱式類型轉換,可以使用強制類型轉換

枚舉
Swift 中枚舉要比 OC 中強大,因為它是一等類型,可以增加屬性和方法
格式:
enum Method { case 枚舉值}
枚舉值可以連在一起寫 case A, B, C, D.
可以使用枚舉類型常量或變量接收枚舉值
利用 Switch 匹配, 如果 case 包含了所有的枚舉值,不需要寫 default,如果沒有,必須寫default。
原始值
oc 中枚舉的本質就是整數(shù),所以有原始值,從0開始
Swift 中的枚舉是沒有原始值的,可以在定義的時候告訴系統(tǒng)讓枚舉有原始值
enum Method:枚舉值原始值類型 {}
Swift 中枚舉值類型除了可以指定整型還可以指定其他類型,但是必須給所有枚舉值賦值,因為不能自動遞增
rawValue 代表將枚舉值轉換為原始值
hashValue 訪問成員值對應的哈希值,不可改變,系統(tǒng)自動生成,Swift 在背后實際上使用哈希值來識別枚舉符號。
通過原始值創(chuàng)建枚舉值 Method(rawValue:),返回的是可選值,因為原始值對應的枚舉值不一定存在,有可能為nil,所以最好使用可選綁定。
枚舉相關值:可以讓枚舉值相關的原始值是一個變量

Swift 內(nèi)存管理
管理引用類型的內(nèi)存,不會管理值類型,值類型不需要管理
內(nèi)存管理原則:當沒有強引用指向,就銷毀
ARC 自動回收內(nèi)存
弱引用,引用計數(shù)不變
如果利用weak 修飾變量,當對象釋放后會被置為 nil
所以利用 weak 修飾的變量必定是一個可選類型。
unowned 無主引用,相當于 OC 中的 unsafe_unretained
unowned 和 weak 的區(qū)別
unowned 修飾的變量在釋放之后不會置為nil,不安全
unowned 修飾的變量不是可選類型。
循環(huán)引用:ARC 不是萬能的,它能夠解決內(nèi)存問題,但某些時候他不能解決內(nèi)存泄漏問題。對象沒有被銷毀,但是我們沒有辦法訪問他們了。
當某一個變量或常量必須有值,一直有值,那么可以使用 unowned 修飾

屬性
存儲屬性:就是 oc 中的普通屬性,在結構體和類中定義的屬性,默認就是存儲屬性
常量存儲屬性:只能在定義或構造的時候修改,構造好之后不能修改
結構體和枚舉是值類型
類是引用類型
不能修改結構體\枚舉常量對象中的值
可以修改類中常量的值,不可以修改類常量的指向
延遲存儲屬性:swift 語言中當構造完一個對象后,對象中所有的存儲屬性都必須要有初始值,但是也有例外,其中延遲存儲屬性可以將屬性的初始化推遲到該屬性第一次被調(diào)用的時候
應用場景:
1.有可能不會用到
2.依賴于其他值
如果不是 lazy 屬性,定義的時候對象還沒有初始化,所以不能訪問self
如果加上 lazy 屬性,代表在使用時才加載,也就是在使用到屬性的時候才會調(diào)用self
而訪問一個類的對象必須使用對象方法,所以訪問時對象已經(jīng)初始化完成了。所以可以使用self。
計算屬性:
計算屬性不直接存儲值,沒有任何的“后端存儲與之對應”
用于計算,可以實現(xiàn)setter 和 getter 兩種計算方法
枚舉不可有存儲屬性,但可以有計算屬性
計算屬性不具有存儲功能,所以不能直接賦值
setter 可以傳遞一個自定義的參數(shù),也可以使用系統(tǒng)默認的參數(shù) newValue
如果使用系統(tǒng)默認參數(shù),必須刪除自定義參數(shù)。
只讀計算屬性
對應 oc 中的 readOnly,只提供 getter 方法。
只讀屬性必須是變量,不能是常量。
只讀屬性可以省略 get{}
應用:只能通過計算獲得,不需要外界設置
屬性觀察器
類似 oc 中的kvo,可以用于監(jiān)聽屬性什么時候被修改,只有被修改時才會調(diào)用。
willSet:在設置新值之前調(diào)用
didSet:在設置新值之后調(diào)用
可以為除計算屬性和lazy屬性之外的屬性添加屬性觀察器,但是可以在繼承類中添加父類計算屬性的觀察器
因為在計算屬性中也可以監(jiān)聽到屬性的改變,所以給計算屬性添加觀察期沒有意義
類屬性:
在結構體和枚舉中使用static
在類中使用 class,并且在類中不允許將存儲屬性設置為類屬性
普通屬性是每個對象一份
類屬性是所有對象共用一份

數(shù)組
有值數(shù)組
空數(shù)組
不可變數(shù)組
可變數(shù)組
獲取長度:.count
判斷是否空:.isEmpty
檢索: [index]
追加:.append or +=
插入: .insert(any:at)
更新: arr[0] = 0
刪除:remove(at:) removeLast removeFirst removeAll(keepingCapacity:Bool)
Range: removeSubrange(0…1) replaceSubrange(0..<1, with:)
遍歷:for number in array for i in 0..<array.count
取出數(shù)組某個區(qū)間的值 array[0..<3]

析構方法
對象的內(nèi)容被回收前隱式調(diào)用的方法,相當于 oc 中的delloc
只要執(zhí)行一些額外操作,例如釋放一些持有資源、關閉文件、斷開網(wǎng)絡
deinit{}
父類的析構方法會被自動調(diào)用,不需要子類管理

subscript 下標:訪問對象中數(shù)據(jù)的快捷方式
所謂下標腳本語法就是能夠通過,實例[索引值]來訪問實例中的數(shù)據(jù)
類似于訪問數(shù)組和字典,Swift 中數(shù)組和字典其實就是結構體
要想實現(xiàn)下標訪問, 必須實現(xiàn) subscript 方法
下標訪問要實現(xiàn) get 方法
下標賦值要實現(xiàn) set 方法

協(xié)議
定義協(xié)議: prtocol ProtocolName {}
協(xié)議可以繼承一個或多個協(xié)議
結構體、枚舉可以實現(xiàn)協(xié)議
類實現(xiàn)協(xié)議和繼承父類,協(xié)議一般寫在父類后面
協(xié)議的屬性:
協(xié)議不指定是否該屬性是一個存儲屬性還是計算屬性,只指定該屬性的名稱和讀寫屬性。屬性要求總是聲明為變量屬性,用 var 關鍵字做前綴。
協(xié)議普通方法實現(xiàn)
協(xié)議可以要求指定的實例方法和類型方法被一致的類型實現(xiàn)。這些方法被寫為定義協(xié)議的一部分。不允許給協(xié)議方法參數(shù)指定默認值。
定義協(xié)議,指定方法要求
實現(xiàn)協(xié)議,實現(xiàn)方法
協(xié)議不僅可以聲明屬性、方法、下標,還可以聲明構造器。但是在 Swift 中除了某些特殊情況,構造器是不會被子類繼承的。所以我們需要在實現(xiàn)協(xié)議要求的構造器的時候,添加 required 關鍵字確保子類也得實現(xiàn)這個構造器。
舉個簡單的栗子,有一只貓和狗,他們都屬于寵物,用類實現(xiàn)的時候,定義一個父類叫寵物,里面有喂食和玩耍兩個方法,貓和狗都繼承于寵物這個父類。這樣操作自然可以實現(xiàn)。但是要知道,貓和狗并不完全是寵物,這里把寵物當做父類就不是很合適,這里應該把寵物定義成協(xié)議就合適很多了
寵物貓和寵物狗,利用協(xié)議可以這樣實現(xiàn)。定義一個 Animal 類,讓貓和狗都繼承于 Animal。再定義一個寵物的協(xié)議,讓貓和狗都實現(xiàn)協(xié)議。
同時繼承父類和協(xié)議的時候,父類要寫在前面。

swift 是面向協(xié)議編程。是因為 swift 中協(xié)議的強大。
與面向對象的不同:
面向對象: 動物-寵物-貓。缺點:寵物可能不需要動物的一些屬性。
面向協(xié)議:動物-貓 協(xié)議:寵物
面向協(xié)議編程:從協(xié)議出發(fā)

extension 協(xié)議 可以給協(xié)議的方法增加默認實現(xiàn)

typealias 與協(xié)議結合的使用
typealias 的作用是給類型進行擴展,它與協(xié)議放在一起會碰撞出不一樣的火花。
協(xié)同協(xié)議的使用
CustomStringConvertible 用于自定義打印
Comparable 協(xié)議用于自定義比較符號
Equatable 協(xié)議用于自定義“==”

協(xié)議是 Swift 非常重要的一部分,蘋果甚至為了他單獨出來—面向協(xié)議編程,利用協(xié)議的優(yōu)點和靈活性可以使項目的架構更加靈活,擁有更加易于延展的架構。

元祖
在其他語言中很早就是有元祖這個概念,但是對于 oc 程序員這是一個新的概念
將多個相同或者不同類型的值用一個小括號括起來就是一個元祖
元祖其實和結構體很像,只是不需要提前定義類型
元祖其實是復合類型,小括號內(nèi)可以寫任意類型
元祖的其他定義方式:

  1. 指明應用元祖元素的名稱。
  2. 通過指定的名稱提取元祖對應的值,會將對應位置的值賦值給對應位置的名稱
    如果不關心元祖中的某個值可以利用 “_”通配符忽略提取
    以前沒有元祖的時候,oc 是通過傳入指針或結構體的方式返回多個值,而有了元祖之后就可以簡單實現(xiàn)讓一個函數(shù)返回多個值
    元祖的定義:
    一元元祖,編譯器會優(yōu)化為其實際元素的類型
    元祖支持嵌套
    可以將元祖的類型重定義為一個類型名
    元祖的數(shù)據(jù)訪問
    當元素未命名時,采用自然序號訪問,序號從0開始
    當元素命名時,可以用命名訪問數(shù)據(jù),當然仍可以使用序號訪問
    可以用多個變量同時進行訪問
    元祖是值類型,元祖的拷貝是值拷貝
    元祖的訪問級別
    取決于它包含的元素,遵循最小的原則。
    元祖元素的修改
    元祖的數(shù)據(jù)不能增刪,但是可以修改。但是不可以修改類型。
    如果是常量定義,元祖的數(shù)據(jù)就不能更改
    如果指定數(shù)據(jù)類型是 Any,那么可以改變類型
    元祖的常見使用場景:
    非常適用于 Dictionary 的遍歷
    非常適合 Array 的 enumrated()
    適合函數(shù)返回多元數(shù)據(jù)
    函數(shù)也可以返回可選元祖
    也可以部分元素可選

運算符
算術運算符
Swift 是安全嚴格的編程語言,會在編譯時候檢查是否溢出,但是只會檢查字面量,不會檢查變量,所以在 Swift 中一定要注意隱式溢出。
Swift 中不允許連續(xù)賦值
區(qū)間:
閉區(qū)間 a…b
半閉區(qū)間: a..<b
區(qū)間只能用于整數(shù),寫小數(shù)有問題

字典
key 一定是可以 hash 的(String, Int, Double, Bool),
哈希就是將字符串變成唯一的整數(shù),便于查找,提高字典遍歷速度
獲取 key 對應的值增加 ??

字符串
計算字符串長度:.lengthOfBytes(using:String.Encoding.utf8)
字符串拼接: stringA + stringB
格式化字符串 (format)
字符串比較:== / != / >= / <=
判斷前后綴:hasPrefix hasSuffix
大小寫轉換:uppercased lowercased
轉換基本數(shù)據(jù)類型:強轉

字符:Character
Swift 使用雙引號
雙引號下只能放一個字符。
oc 中字符串是以\0結尾的。
Swift 并不是

break :跳出循環(huán),無論循環(huán)條件是否保持還為真都不會再執(zhí)行循環(huán)
continue:跳出本次循環(huán),如果循環(huán)條件還為真還會繼續(xù)執(zhí)行循環(huán)

for
stride(from:to:by)遍歷,跨步,開區(qū)間
stride(from:through:by)遍歷,跨步,閉區(qū)間

Swift4 語法
struct 也支持 kvc
KVO: 目前依然只有 NSObject 才支持 KVO
被觀察的屬性需要用 dynamic 修飾,否則也無法觀察到

斷言:當程序發(fā)生異常時,如果希望找到出錯位置并打印一條消息,就可以使用斷言,即通過一個全局的函數(shù) assert
assert 接受一個閉包做為其第一個參數(shù),第二個參數(shù)是一個字符串。加入第一個閉包的返回值是false,那么這個字符串就會被打印到中控制臺上。
assert(()->Bool.”Message”)
precondition:它和 assert 的格式類型,也是動態(tài)的,它會造成程序的提前終止并拋出錯誤信息;
使用擴展的方法向數(shù)組增加一個方法判斷下標是否越界。
precondition 在一般代碼中并不常見,因為它是動態(tài)的,只會在程序運行時進行檢查。適用于那些無法在編譯器確定的風險情況

switch
在 case 中定義變量要加大括號,否則作用域混亂
可以判斷對象類型,不可以穿透,不能不寫 default,位置必須在最后;定義變量不需要加大括號
區(qū)間
元祖匹配
值綁定
根據(jù)條件綁定

while

swift 不需要 import 類名也可以使用這個類

參數(shù)名稱縮寫
Swift 自動為內(nèi)聯(lián)閉包提供參數(shù)縮寫功能,你可以使用 $0,$1, m2 依次獲取閉包的第1,2,3個參數(shù)

CGRect CGRectDivide
將一個 CGRect 在一定位置切分成兩個區(qū)域
CGRectDivede(rect,&small,&large,20,CGRectMinXEdege)
將 rect 分割成兩部分,{0,0,20,100} {20,0,80,100}

swifter 王巍
swift 新元素

柯里化(Currying)
如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個函數(shù)
函數(shù)式編程在把函數(shù)當做一等公民的時候,就不可避免的會產(chǎn)生“柯里化”這種用法
柯里化是量產(chǎn)相似方法的好辦法

protocol 的方法聲明為 mutating
如果沒在協(xié)議方法里寫 mutating 的話,別人如果用 struct 或 enum 實現(xiàn)這個協(xié)議的時候就無法在方法里面改變自己的變量了

Sequence
swift 中 for in 這種方式的遍歷是針對 sequenceType 這個 protocol 的。為了實現(xiàn) Sequence 首先需要實現(xiàn)一個 IteratorProtocol。
Sequence 協(xié)議擴展已經(jīng)實現(xiàn)了 map,filter,reduce
map 方法 可以用在 Optionals 和 SequenceType 上,可以把數(shù)組元素轉變類型
filter 用于選擇數(shù)組元素中滿足某種條件的元素
reduce 用于把數(shù)組元素組合計算為一個值
flatMap 返回一個將變換結果連接起來的數(shù)組(壓平)。空值過濾。

多元組(Tuple)
應用:交換輸入 (a, b) = (b, a)
let rect = CGRect(x:0,y:0,width:100,height:100)
let (small,large) = rect.divided(atDistance:20,from: .minXEdge)

@autoclosure 和 ??
@autoclosure 可以說是 Apple 的一個非常神奇的創(chuàng)造,因為這更多的像是在“hack”這門語言
就是把一句表達式自動的封裝成一個閉包,這樣有時候在語法上看起來就會很漂亮。
logIfTrue(2>1)
不支持帶有輸入?yún)?shù)的寫法
?? 這個操作符可以判斷輸入并在當左側的值是非 nil 的 Optional 值時返回其 value,否則返回右側的值。
內(nèi)部實現(xiàn):defaultValue 就是一個 @autoclosure 修飾的參數(shù)。
為什么不直接傳入 T 參數(shù)?
是因為如果這個默認值是經(jīng)過一系列計算得到的話,可能會造成浪費。
為什么會浪費?
是因為當 optional 不是 nil 的話,我們實際上是完全沒有用到這個默認值。而是直接返回解包之后的值。這樣的開銷是完全可以避免的。方法既是將默認值的計算推遲到了 optional 判定為 nil 之后

@escaping
最簡單的 block 做為參數(shù)的調(diào)用,默認隱藏了一個假設,就是參數(shù)中 block 會在 方法返回前完成。也就是說對 block 的調(diào)用是同步行為。如果我們改變一下代碼,將 block 放到一個 dispatch 中,讓它在方法返回后被調(diào)用的話,我們就需要在 block 類型前面加上 @escaping 標記來表明這個閉包是會逃逸出該方法的。
沒有逃逸行為的閉包,因為作用域就在函數(shù)內(nèi)部,所以不用擔心閉包內(nèi)部持有 self。
有逃逸行為的閉包,Swif 強制我們寫明 self,以起到提醒作用,我們就需要考慮 self 的持有情況。如果我們不希望在閉包中持有 self,可以使用 【weak self】的方式來表達。
這時,在閉包執(zhí)行時已經(jīng)沒有對實例的應用,因此輸出為 nil。

Optional Chaining 可選鏈
Optional Binding 可選綁定
使用可選鏈可以讓我們擺脫很多不必要的判斷和取值
因為可選鏈隨時都可能提前返回 nil ,所以使用 optional chaining 所得到的東西都是 optional 的。
使用 optional Binding 來判定方法是否調(diào)用成功

操作符
與 OC 不同,swift 支持重載操作符這樣的特性。最常見的使用方式就是定義一些簡便的計算。比如二維向量的數(shù)據(jù)結構的操作。
如果想要定義一個全新的操作符,需要自己來做聲明。
precedencegroup:操作符優(yōu)先級別
associativity:結合律
higherThan:運算的優(yōu)先級
infix:表示定義的是一個中位運算符,即前后都是輸入。
Swift 的操作符是不能定義在局部域中的,因為至少為希望能在全局范圍內(nèi)使用你的操作符,否則操作符也就失去意義了.
在重載或者自定義操作符的時候。應當盡量將其作為其他方法的“簡便寫法”,避免在其中實現(xiàn)大量邏輯或提供獨一無二的功能。
來自不同 module 的操作符是有可能沖突的,在使用重載或者自定義操作符時,請先再三權衡斟酌,你或你的用戶是否真的需要這個操作符。

func 的參數(shù)修飾
Swift 是一門討厭變化的語言,所有有可能的地方,都被默認為是不可變的。這樣不僅可以確保安全,還可以在編譯器的性能優(yōu)化上更有作為。
有時候我們希望在方法內(nèi)部直接修改輸入的值,這時候我們可以用 inout 來對參數(shù)進行修飾。在前面加上 & 符號。
值類型,并不能直接修改它的地址來讓它指向新的值。

字面量表達
指像特定的數(shù)字、字符串或者是布爾值這樣,能夠直截了當指出自己類型并為變量賦值的值。
在 Swift 中,Array 和 Dictionary 在使用簡單的描述賦值的時候,使用的也是字面量。
Swift 為我們提供了一組非常有意思的協(xié)議,使用字面量來表達特定的類型。對于那些實現(xiàn)了字面量表達協(xié)議的類型,在提供字面量賦值的時候,就可以簡單de按照協(xié)議方法中定義的規(guī)則“無縫對應”的通過賦值的方式將值表達為對應類型。

下標
在絕大多數(shù)語言中使用下標讀寫類似數(shù)組或者字典這樣的數(shù)據(jù)結構。
在字典中使用下標訪問得到的結果是一個 optional 的值。
做為一門代表了先進生產(chǎn)力的語言,Swift 是允許我們自定義下標的。不僅包含對自己寫的類型進行下標定義,也包含那些已經(jīng)實現(xiàn)下標訪問的類型進行擴展。
向數(shù)組添加一個接受數(shù)組下標輸入的讀取方法。不推薦使用這樣的方式。

方法嵌套
方法成為了一等公民,這樣就可以把方法做為變量或者參數(shù)來使用了。我們可以在方法內(nèi)定義新的方法,這個代碼結構層次和訪問級別的控制帶來了新的選擇。
另一個重要的考慮是有些方法我們完全不希望在其他地方被直接使用。

命名空間
oc 一直以來令人詬病的地方就是沒有命名空間,在應用開發(fā)時,所有的代碼和引用的靜態(tài)庫都會被編譯到同一個域和二進制中。這樣的后果一旦我們有重復的類名的話,就會導致編譯時的沖突和失敗。
在 Swift 中可以使用命名空間了,即使是名字相同的類型,只要是來自不同命名空間的話,就會和平共處的。Swift 的命名空間是基于 module 而不是在代碼中顯示地指明,每個 module 代表了 Swift 中的一個命名空間。

typealias
為已經(jīng)存在的類型重新定義名字。可以使用 typealias ,給類型取一個別名,增加代碼的可讀性。可以不再關心代碼里那成堆的 Int 和 string 之類的基本類型代表的是什么東西。
typealias 是單一的,不能將整個泛型類型進行重命名。
另一個使用場景是某個類型同時實現(xiàn)多個協(xié)議的時候,我們可以用 & 符號連接協(xié)議。然后給一個新的更符合上下文的名字,增強代碼可讀性
typealias Pet = Cat & Dog

associatedtype
使用 associatedtype 可以在協(xié)議中添加一個限定,來指定事物的具體類型。在 Tiger 通過 tyoealias 具體指定 F 為 Meat 之前, 只需要滿足協(xié)議的類型中 F 和 eat 參數(shù)一致即可。不過這里忽視了被吃的必須是 Food 類型這個前提。associatedtype 聲明中可以使用冒號來指定類型滿足某個協(xié)議。另外,在 Tiger 中只要實現(xiàn)了正確類型的 eat,F(xiàn) 的類型都可以推算出來,不需要顯式的寫明 F。
在添加 assoicatedtype 之后,Animal 協(xié)議就不能被當做獨立的類型使用了。這是因為 Swift 在編譯的時候需要確定所有類型。在一個協(xié)議加入了 associatedtype 或者 Self 的約束之后,它將只能被用為泛型約束,而不能做為獨立類型的占位使用,也是去了動態(tài)派發(fā)的特性。

可變參數(shù)函數(shù)
就是可以接受任意多個參數(shù)的函數(shù)。我們最熟悉的就是 NSString 的 stringWithFormat: 方法了。
在 Swift 中寫一個可變參數(shù)的函數(shù)只需要在聲明參數(shù)時在類型后面加上 … 就可以啦。輸入的參數(shù)在函數(shù)體內(nèi)部將被做為數(shù)組來使用。
在 Swift 中可以隨意的放置可變參數(shù)的位置,而不必拘泥于最后一個參數(shù)。
在同一個方法中只能有一個參數(shù)是可變的,可變參數(shù)都必須是同一個類型的。
數(shù)組的兩個便利方法:forin forEach
當遍歷的時候有 return 關鍵字的時候就會有區(qū)別

初始化方法順序
設置自己需要初始化的參數(shù)
調(diào)用父類相關的初始化方法
對父類中的需要改變的成員進行設定
如果沒有第三步,第二步也可以省略。默認最后調(diào)用第二步。
Swift 中的初始化想要達到什么樣的目的。
其實就是安全。Swift 有超級嚴格的初始化方法。不加修飾的 init 方法都需要在方法中保證所有非 Optional 的實例變量被賦值初始化,而在子類也強制(顯示或隱式)調(diào)用 super 版本的 designated 初始化,所以無論如何走何種路徑,被初始化的對象總是可以完成完整的初始化。
在 init 方法里面我們可以對 let 的實例常量進行賦值,這是初始化的重要特點.這是因為在 Swift 中 init 方法只會被調(diào)用一次。
在 init 前面加上 convenience 關鍵字的初始化方法是 Swift 中的“二等公民”,所有的 convenience 的方法都必須調(diào)用同一個類中的 designated 初始化完成設置,另外,convenience 方法不能被子類重寫也不能從子類使用 super 調(diào)用
只要在子類中實現(xiàn)了父類 convenience 所需的 init 方法,我們在子類中就可以使用父類的 convenience 的初始化方法了。
對于某些我們希望子類中一定實現(xiàn)的 designated 方法,可以通過添加 required 關鍵字進行限制,強制子類對這個方法重寫實現(xiàn)。可以保證依賴于某個 designated 初始化方法的 convenience 一直可以被使用。
對于 convenience 方法我們也可以加上required 以確保子類對其實現(xiàn)。這在要求子類不直接使用父類中的 convenience 初始化方法時有幫助。

初始化返回 nil
Swift 中我們可以在 init 聲明時在其之后加上 ? 或者 !來表明初始化失敗可能會返回一個 nil。所有的結果都將是 Optional 類型,我們可以通過 Optional Binding,就能知道是否初始化成功,并安全的使用它們了。 我們在這類初始化方法中還可以對 self 賦值,也算是 init 方法里的特權之一

static 和 class
表示“類型范圍作用域”。類變量/類方法,靜態(tài)變量/靜態(tài)函數(shù)。非 class 中,使用 static 來描述類型作用域。包括在 enum 和 struct 中描述類型方法和類型屬性時。在這兩個值類型中,我們可以在類型范圍內(nèi)聲明并使用存儲屬性、計算屬性和方法。
class 關鍵字是專門用在 class 類型的上下文中的。用來修飾類方法和類計算屬性。但不能出現(xiàn) class 的存儲屬性。
如果我們想在 protocol 中實現(xiàn)一個類型域上的方法或者計算屬性的話,應該用 static 關鍵字。實現(xiàn)的時候,不管在什么結構類型中,使用 static 是沒有問題的。

多類型和容器
Swift 中原生容器類型有三種。它們分別是 Array、Dictionary 和 Set;
它們都是泛型的,也就是說我們在一個集合中只能放入同一個類型的元素。
如果我們要把不相關的類型放入到同一個容器中的話,需要做一些轉化工作。
這樣的轉換會造成部分信息的缺失,我們從容器中取值時只能取到信息完全丟失之后的結果。在使用時還需要進行一次類型轉換。這其實是無其他可選方案后的最差選擇,因為這樣的轉換之后,編譯器就不能再給我們提供警告信息了。我們可以隨意添加,也可以將取出的值隨意轉換,這是一件非常危險的事情。
我們還可以在容器中添加實現(xiàn)了同一協(xié)議的類型的對象,對于對象中存在某種共同特性的情況下無疑是最方便的。
另一種方式是利用 enum 可以帶有值得特點,將類型信息封裝到特定的 enum 中。

default 參數(shù)
Swift 的方法是支持默認參數(shù)的,也就是在聲明方法的時候,可以給參數(shù)制定一個默認使用的值。
在調(diào)用的時候,我們?nèi)绻胍褂媚J值的話,只要不出入相應的值就可以了。
默認參數(shù)寫的是 default,這是含有默認參數(shù)的方法所生成的 Swift 的調(diào)用接口。當我們指定一個編譯時就能確定的常量做為默認參數(shù)的取值的時候,這個取值是隱藏在方法內(nèi)部,而不應該暴露給其他部分。
舉個例子:NSLocalizedString(key:String, tableName:String? = default,..)
assert(@autoclosure condition:()->Bool,@autoclosure _ message:()->String = default..)

正則表達式
作為一門先進的編程語言,Swift 可以說是吸收了眾多其他先進語言的特點,但是讓人略微失望的,就是 Swift 至今沒有在語言層面上支持正則表達式。就像 Perl 或者 Ruby 那樣的語言使用比如 =~ 這樣的符號來進行正則匹配。
最簡單的想法自然是自定義 =~ 這個運算符。在 Cocoa 中我們可以使用 NSRegularExpression 來做正則匹配。我們可以先寫一個接受正則表達式的字符串,以此生成 NSRegularExpression 對象,然后使用該對象來匹配輸入字符串,并返回結果告訴調(diào)用者是否匹配成功。

模式匹配
Swift 中的模式匹配只能支持最簡單的相等匹配和范圍匹配。使用 ~= 來表示模式 匹配的操作符。
在操作符兩邊分別接受可以判等的類型,可以與 nil 比較的類型,已經(jīng)一個范圍輸入和某個特定值,返回值很明了,都是是否匹配成功的 Bool 值。
Swift 的 switch 就是使用了 ~= 操作符進行模式匹配。在 switch 中做 case 判斷的時候,我們完全可以使用自定義的模式匹配方法來進行判斷。

… 和 ..<
在很多腳本語言中,都有很多類似 0..3 或者 0…3 這樣的 Range 操作符,用來簡單指定一個從 X 開始連續(xù)計數(shù)到 Y 的范圍。
最基礎的用法當然是在兩邊指定數(shù)字。0…3 表示從 0 開始 到 3 為止包含 3 這個數(shù)字的范圍,成為全閉合的范圍。 0..< 3 是不包含最后一個數(shù)字的范圍。對于這樣得到的數(shù)字的范圍,我們可以對他進行 for…in 的訪問。
看看 Swift 對這兩個操作符的定義,可以發(fā)現(xiàn)都是支持泛型的。可以接受 comparable 的輸入,返回 ClosedInterval 或者 HalfOpenInterval 。在 Swift 中除了數(shù)字另一個實現(xiàn) comparable 的基本類型就是 String。
例子:
確認某個單詞全部字符都是小寫英文字母 interval = a…z
是不是有效的 ASCII 字符 \0…~ ,分別是 ASCII 的第一個和最后一個字符。

AnyClass,元類型和 .self
在 Swift 中能夠表示“任意”這個概念的除了 Any 和 AnyObject 外,還有 AnyClass。
AnyClass 在 Swift 中被一個 typealias 定義:typealias AnyClass = AnyObject.Type
。這樣得到的是一個元類型。在聲明時我們總是在類型后面加上 .Type。代表這個類型的類型。從 A 中取出其類型的時候,我們需要用到 .self。
元類型或者元編程在我們編寫某些框架性的代碼時非常方便。可以通過讀入配置文件之類的方式實現(xiàn)。
DSL:領域專屬語言的方式。不觸及源碼的情況下,很簡單的完成一系列復雜的操作。
在注冊 tableview 的 cell 的類型的時候就需要輸入 AnyClass 。
獲得協(xié)議的元類型:在protocol 的名字后面使用 .Protocol 獲取。

協(xié)議和類方法中的 Self
在聲明協(xié)議的時候,我們希望在協(xié)議中使用的類型就是實現(xiàn)這個協(xié)議本身的類型的話,就需要使用 Self 進行指代。
使用 type(of:)初始化,保證方法與當前類型上下文無關。
在類方法中也可以使用 Self。核心在于保證子類也能返回恰當?shù)念愋汀?/p>

動態(tài)類型和多方法
Swift 中我們雖然可以通過 dynamicType 來獲取一個對象的動態(tài)類型,但是在使用中,Swift 現(xiàn)在是不支持多方法,不能根據(jù)對象在動態(tài)時的類型進行合適的類型方法調(diào)用。方法的調(diào)用只在編譯時決定。
Swift 我們可以重載同樣名字的方法,而只需要保證參數(shù)類型不同。

屬性觀察
利用屬性觀察我們可以在當前類型內(nèi)監(jiān)視對于屬性的設定。Swift 為我們提供兩個屬性觀察的方法,willSet 和 didSet。
屬性觀察的一個重要用處是作為設置值的驗證。
存儲屬性會在內(nèi)存中實際分配地址對屬性進行存儲,而計算屬性則不包括背后的存儲,只是提供 set 和 get 兩種方法。
在同一類型中屬性觀察和計算屬性是不能同時共存的。如果我們無法改動這個類,又想要通過屬性觀察做一些事情的話,可能就需要子類化這個類,并且重寫它的屬性。重寫的屬性并不知道父類屬性的具體情況,只是繼承了屬性的名稱和類型,因此在子類的重載屬性中我們是可以添加對父類屬性的屬性觀察,而不用在意父類中的屬性到底是計算屬性還是存儲屬性。
didSet 中會用到 oldValue,而這個值需要在 set 之前獲取并存儲,否則無法保證正確性。

final
final 關鍵字可以用在 class 、func 、var 前面進行修飾,表示不允許對該內(nèi)容進行繼承或重寫操作。
為了父類中某些代碼一定會被執(zhí)行。
有時候父類的方法在被繼承之后必須執(zhí)行的,如果子類重寫了父類的方法,是沒有辦法強制子類方法一定會去調(diào)用相同的父類方法的。
子類繼承和修改是一件危險的事情

lazy 修飾符和 lazy 方法
我們在使用 lazy 做為屬性修飾符時,只能聲明屬性是變量,并且顯式的指定屬性類型,還有一個給屬性賦值的語句在首次訪問屬性時運行。
在 Swift 標準庫中,還有一組 lazy 方法。這些方法可以配合 像 map 或 filter 這類接受閉包并進行運行的方法一起,讓整個行為變成延遲執(zhí)行。

隱式解包 Optional
相對于普通的 Optional ,在 Swift 中還有一種特殊的 Optianal 類型,對他的成員或者方法進行訪問的時候,編譯器會幫我們自動進行解包,也就是 ImplicitlyUnwrappedOptional。在聲明的時候我們可以在類型后面加上一個(!)來告訴編譯器我們需要一個可以隱式解包的 optional 值
如果 這個 optional 值是 nil 的話不加檢查的寫法調(diào)用會導致程序奔潰。這是一種危險寫法
因為 oc 的 Cocoa 的所有類型變量都可以指向 nil,在將 Cocoa API 從 oc 轉移到 Swift 時,無法判定是否存在 nil 的可能。這時候 ,Optional 隱式解包就作為一種妥協(xié)方案出現(xiàn)了。使用隱式解包的最大好處是對于那些我們能確定的 api 來說,我們可直接訪問屬性和調(diào)用方法,會很方便。這是一種簡單但是危險的使用方式了。
現(xiàn)在比較常見的隱式解包就只有 IBOutlet 了
少用 隱士解包 optional ,推薦多寫 optional binding。

多重 Optional
optional 類型是一個 enum。Optional<T> 這個 T 可是任意類型,亦可以是一個 Optional。
如果我們把 optional 比作一個一個盒子,那么打開這個盒子之后可能的結果會有空氣、糖果,或者是另一個盒子。
使用 fr v -R 可以打印 optional 值

Optional Map
map 方法可以在 collectionType(這是一個協(xié)議) 的 extension 中找到定義
在其他語言中 map 是常見常用的一種語言特性了。
在 optional 的聲明中,也有一個 map 方法。這個方法可以讓我們方便的對可選值做變化和操作,而不用手動解包。
正符合函數(shù)式編程函子的概念。函子指的是可以被函數(shù)使用,并映射為另一組結果,而這組結果也是函子的值。

Protocol Extension
在 Swift 中標準庫的功能都是基于 protocol 來實現(xiàn)的,舉個例子,Array 就是遵守了 collectionType 這個協(xié)議的。 CollectionType 是一個非常重要的協(xié)議,除了 Array 以外,Dictionary 和 Set 也都實現(xiàn)了這個協(xié)議所定義的內(nèi)容。
為實現(xiàn)了某個協(xié)議的所有類型添加一些另外的共通的功能。
我們可以對一個 Protocol 進行擴展,而擴展中實現(xiàn)的方法將作為實現(xiàn)擴展的類型的默認實現(xiàn)。在具體的實現(xiàn)這個協(xié)議的類型中,即使什么都不寫,也可以編譯通過。調(diào)用的時候會直接使用 extension 中的實現(xiàn)。
protocol extension 為 protocol 中定義的方法提供了一個默認實現(xiàn)。

where 和模式匹配
在 switch 語句中,使用 where 來限定某些條件 case,這可以說是模式匹配的標準用法。
在 for in中 可以使用 where 做類似的條件限定
在 Swift 3 中,if let 和 guard let 的條件判斷不再使用 where 語句,而是和普通的條件判斷一樣,用逗號寫在后面。
這兩種方式都可以用另外的 if 來替代,只不過讓我們的代碼更加的易讀了。也有一些場合只有 where 才能準確表達的。比如我們在泛型中想要對方法的類型進行限定的時候。
例如 :標準庫里對 RawRepresentable 協(xié)議定義 != 運算符。
當我們希望一個協(xié)議擴展的默認實現(xiàn)只在一些特定的情況下適用,我們就可以用 where 關鍵字來進行限定。
例如:Sequence 的 sorted 方法就被限定在這樣一個類型限定的協(xié)議擴展中。

indirect 和嵌套 enum
涉及到數(shù)據(jù)結構的理論和模型(鏈表、樹、圖)時,我們往往會用到嵌套的類型。

2.從 Objective-C 到 Swift
Selector
@selectior 是 oc 時代的關鍵字,它可以把一個方法轉換為 SEL 類型,它的表現(xiàn)很類似一個動態(tài)的函數(shù)指針。
如果要追求靈活的話,我們更愿意用 NSSelectiorFromString ,因為我們可以在運行時動態(tài)生成字符串,從而通過方法的名字調(diào)用到方法。
在 Swift 中,我們用 #selector 從暴露給 oc 的方法中獲取方法名字。對應原來的 SEL 是一個叫 selector 的結構體。
需要注意的是,selector 其實是 OC runtime 的概念。如果你的 selector 只對 Swift 可見的話(也就是一個 private 方法),在調(diào)用這個方法的時候你會遇到一個 unrecognized selector 的錯誤。正確的方法是在 private 前面加上 @objc 關鍵字,這樣在運行時就可以找到方法了。
值得一提的是,如果方法名字在方法所在域內(nèi)是唯一的話,我們可以只是用方法名字做為 #selector 的內(nèi)容。相比于帶有冒號寫法的完整形式來說,這么寫起來會方便一些。
但是如果同一個作用域中存在相同名字的方法,我們可以使用強制轉化來區(qū)分它們。

實例方法的動態(tài)調(diào)用
在 Swift 中有這樣一種寫法,可以不直接使用實例來調(diào)用這個實例的方法,而是通過類型取出這個類型的某個實例方法的簽名,然后再通過傳遞實例來拿到實際需要調(diào)用的方法。
Swift 可以用 Type.instanceMethod 的語法來生成一個柯里化的方法。
如果遇到有類型方法的沖突時,默認取到的是類型方法,如果想要取得實例方法,可以顯式聲明類型。

單例
對于一些希望能在全局方便訪問的實例,或者在 app 的生命周期內(nèi)只應該存在一個的對象,我們一般使 用單例進行存儲和訪問。
在 oc 中使用 GCD 保證代碼只執(zhí)行一次。保證單例在線程上的安全。
可以用dispatch_once 保證線程安全,但是在 Swift 中其實有一種更簡單的保持線程安全的方式,就是 let。
Swift 1.2 之前不支持 static let 和 static var 這樣的存儲類變量。在 1.2 中支持了類變量。
在初始化變量的時候,Swift 會把初始化包裝在 swift_once_block_invoke 中,保證唯一性。
在類型中加入了一個私有的初始化方法,來覆蓋默認公開的初始化方法,這使的項目中的其他地方不能通過 init 初始化單例實例,保證了類型單例的唯一性。
如果你需要的是類似 default 的形式的單例(也就是可以創(chuàng)建自己的實例),可以去掉這個私有的 init 方法。

條件編譯
在 c 系語言中,可以使用 #if #ifdef 之類的編譯條件分支來控制哪些代碼需要編譯。Swift 沒有宏定義的概念,因此不能用 #ifdef 方法來檢查某個符號是否經(jīng)過宏定義。

if #else #endif

編譯標記
// MARK: 粗體標簽的形式將名稱顯示在導航欄
// TODO:還沒完成
// FIXME:需修改
Swift 中沒有類似 OC 的 #warning

@UIApplicationMain
調(diào)用方法,根據(jù)第三個參數(shù)初始化一個 UIApplication 或其子類的對象并開始接受事件(傳入nil。默認 UIApplication),最后一個參數(shù)指定了 Appdelegate 作為應用的代理。用來接收 didFinishLaunching 或者 didEnterBackground 這樣的與應用生命周期相關的委托方法。
Swift 中在默認的 Appdelegate 類的聲明上方有一個 @UIAppdelegateMain 的標簽。這個標注做的事情就是將標注的類作為委托。創(chuàng)建一個UIapplication并啟動程序。
Swift 中也可以有一個 main.swift 的文件,不需要定義作用域,直接書寫代碼。這個文件中的代碼將作為 main 函數(shù)執(zhí)行。
我們可以將第三個參數(shù)替換為我們自己的 UIApplication 子類,這樣我們就可以輕易做一些控制整個應用行為的事情了。
例如:每次發(fā)送事件(點擊按鈕),我們都可以監(jiān)聽到這個事件了。

@objc 和 dynamic
雖然說 Swift 的初衷是擺脫 oc 沉重的歷史包袱,但是不可否認的是,經(jīng)過二十多年的洗禮,Cocoa 框架早就烙上了不可磨滅的 oc 的印記。無數(shù)的第三方庫是 oc 寫成的,這些積累不容小覷。所以 Swift 做了與 oc 的兼容
Apple 的做法是允許我們在同一個項目中同時使用 Swift 和 oc 開發(fā)。其實一個項目中的 Swift 和 oc 是處于兩個世界中的,為了能夠讓他們能夠互通,我們需要添加一些橋梁。
通過添加{product-module-name}-Bridging-Header.h 文件,并在其中填寫想要使用的頭文件名稱,我們就可以在 Swift 中使用 oc 代碼了。
如果想要在 oc 中使用 Swift 代碼,可以直接導入自動生成的頭文件 {product-module-name}-Swift.h 來完成。
oc 對象是基于運行時,骨子里遵循了 KVC 和動態(tài)派發(fā),在運行時再決定具體實現(xiàn)。Swift 為了追求性能,類型和方法在編譯時就已經(jīng)決定,運行時不再需要經(jīng)過一次查找,可以直接使用。
在 Swift 類型中,我們可以把需要暴露給 oc 的任何地方加上 @objc 修飾符。

可選協(xié)議和協(xié)議擴展
Swift 中的 protocol 的所有方法都必須被實現(xiàn),那些如果沒有實現(xiàn)協(xié)議就無法正常工作的方法一般是必須的,而相對的像做為事件通知或對非關鍵屬性進行配置的方法一般都是可選的。
如果我們想要將 Swift 像 oc 那樣定義可選的協(xié)議方法,就需要將協(xié)議本身和方法都定義成 oc 的。也就是在 protocol 定義之前已經(jīng)協(xié)議方法之前加上 @objc。和 oc 不同的是我們使用 optional 來定義可選方法。
對于所有的聲明,它們的前綴都是分開的,也就是說你不能像在 oc 里那樣用一個 @optional 指定接下來的若干個方法都是可選的了。必須對每一個可選方法添加前綴,對于沒有前綴的方法來說,它們是默認必須實現(xiàn)的。
一個不可避免的限制是,使用 @objc 修飾的 protocol 就只能被 class 實現(xiàn)了。對于 struct 和 enum 來說。我們是無法讓他們所實現(xiàn)的協(xié)議中含有可選方法或者屬性的。另外,實現(xiàn)它的 class 中的方法還必須被標記為 @objc。或者整個類繼承自 NSObject。
在 Swift 2.0 后,我們有了另一種選擇,那就是使用 protocol extension。我們可以在聲明一個 protocol 之后再用 extension 的方式給出部分方法的默認實現(xiàn)。這樣這些方法在實際的類中就是可選實現(xiàn)了。

內(nèi)存管理,weak 和unowned
Swift 是自動管理內(nèi)存的,初始化創(chuàng)建一個對象時,替我們管理和分配內(nèi)存。釋放的原則遵循自動引用計數(shù)的原則:當一個對象沒有引用的時候,其內(nèi)存將會被自動回收。這套機制從很大程度上簡化了我們的代碼,我們只要保證在合適的時候將引用置空,就可以確保內(nèi)存不會出錯。
但是所有自動引用計數(shù)機制都有一個無法繞過的限制,那就是循環(huán)引用。
在 Swift 里防止循環(huán)引用,我們必須給編譯器一點提示,表示我們不希望它們互相持有。一般來說我們希望“被動”的一方不要去持有“主動”的一方。
unowned 更像是 oc 的 unsafe_unretained。unowned 設置以后即使它原來引用的內(nèi)容已經(jīng)被釋放了,它仍然會保持對被已經(jīng)釋放了的對象的一個“無效的”引用。他不能是 optional 值,也不會指向 nil。如果你嘗試調(diào)用這個引用的方法或者訪問成員屬性的話,程序就會崩潰。而 weak 就友好些,在引用的內(nèi)容被釋放之后,標記為 weak 的成為會被置為 nil(所以被標記為 weak 的變量一定是 optional ),關于兩者的選擇,Apple 給我們的選擇是如果能確保使用時不會被釋放,盡量使用 unowned,如果存在被釋放的可能,那就選擇用 weak。
閉包循環(huán)引用。如果我們可以確定在整個過程中 self 不會被釋放的話,可以把 weak 改為 unowned ,這樣可以不判斷 StrongSelf。
這種在閉包參數(shù)的位置進行標注的語法結構是將要標注的內(nèi)容放在參數(shù)的前面,并使用中括號括起來。如果有多個需要標注的元素,用逗號隔開。

@autoreleasepool
Swift 在內(nèi)存管理上使用的是自動引用計數(shù)的一套方法,在 ARC 下雖然不需要手動的調(diào)用 retain, release 這樣的方法來管理引用計數(shù),但是這些方法還是會被調(diào)用,只是編譯器在編譯時幫我們在合適的地方加入了而已。
在 app 中,整個主線程是在自動釋放池中運行的,
autoreleasepool 的使用情況。1.再循環(huán)中一直不斷創(chuàng)建 autorelease 對象。可以再循環(huán)中加入釋放池。但是每一次循環(huán)都生成自動釋放池,雖然可以保證內(nèi)存使用達到最小,但是釋放過于頻繁可能回帶來性能憂慮。
在 swift 中更提倡用初始化的方法而不是用類方法來生成對象。使用初始化的話,我們就不需要面臨自動釋放的問題。每次超過作用域,自動內(nèi)存管理將會為我們做好內(nèi)存相關的事情。

值類型和引用類型
Swift 的類型分為值類型和引用類型兩種,值類型在傳遞和賦值時將進行復制,而引用類型則只會使用引用對象的一個“指向”。struct 和 enum 是值類型,class 是引用類型。Swift 中的內(nèi)建類型都是值類型,不僅包括傳統(tǒng)意義像 Int,Bool,甚至 String, Array, Dictionary 都是值類型。
使用值類型的好處。一個顯而易見的優(yōu)勢就是減少了堆上內(nèi)存分配和回收的次數(shù)。Swift 的值類型,特別是數(shù)組和字典這樣的容器,在內(nèi)存管理上經(jīng)過了精心的設計。值類型的一個特點是在傳遞和賦值時進行復制,肯定會產(chǎn)生額外開銷,但是在 Swift 中這個消耗被控制在了最小范圍,在沒有必要復制的時候,值類型的復制都是不會發(fā)生的。也就是說,簡單的復制,參數(shù)的傳遞等等普通的操作,雖然我們可能用不同的名字來回設置和傳遞值類型,但是在內(nèi)存上他們都是同一塊內(nèi)容。
值類型被復制的時機是值類型的內(nèi)容發(fā)生改變時。
值類型在復制時,會將存儲在其中的值類型一并進行復制,而對于其中的引用類型來說,則只復制一份引用。
雖然將數(shù)組和字典設置為值類型最大的考慮是為了線程安全,但是這樣的設計在存儲的元素或條目較少時,給我們帶來一個優(yōu)點,就是非常高效。但是在少數(shù)情況下,當容器內(nèi)容比較多,并且還要對內(nèi)容進行增加或刪除,在這時,Swift 內(nèi)建的值類型的容器在每次操作的時候都要復制一遍。這個開銷就就不容忽視。幸好我們有 Cocoa 中的引用類型來應對,就是 NSMutable。

String 還是 NSString
像 String 這樣的 Swift 類型對于 Foundation 對應的類是可以無縫轉換的。
Swift 中 string 是 struct 類型,相比起 NSObject 的 NSString 類來說,更切合字符串的不變特性。
使用 String 唯一一個比較麻煩的地方在于他和 Range 的配合。在使用 String 對應的 API ,NSRange 會被映射成他在 Swift 中且對應 String 的特殊版本:Range<String.Index>.這時候就非常討厭。這種情況下,將 String 轉為 NSString 是個不錯的選擇

UnsafePointer
不安全的指針
指針在 Swift 中并不被提倡,語言標準中也是完全沒有與指針完全等同的概念的。為了與龐大的 c 系帝國進行合作,Swift 定義了一套對 C 語言指針的訪問和轉換方法。就是 UnsafePointer 和他的一系列變體。對于使用 C API 時如果遇到接受內(nèi)存地址做為參數(shù),或者返回是內(nèi)存地址的情況,在 Swift 中會轉為 UnsafePoin<Type> 的類型。
const 對應 UnsafePointer,可變對應 UnsafeMutablePointer
在 C 中,對某個指針進行取值使用 *,在 Swift 中使用 meemory 屬性讀取相應內(nèi)存中存儲的內(nèi)容。通過傳入指針地址進行方法調(diào)用就比較類似了,都是 &。

C 指針內(nèi)存管理

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373