讓我們來思考一個(gè)問題,就是 Swift 的核心是什么?
不知道大家有沒有看過 WWDC 2015 的視頻,其中有一個(gè)編號為 408 的視頻解釋了這個(gè)問題,下面是視頻鏈接:Protocol-Oriented Programming in Swift。
視頻中介紹了從 OOP(面向?qū)ο缶幊蹋?到 POP(面向協(xié)議編程)的轉(zhuǎn)變過程。
Swift is a Protocol-Oriented Programming Language
Swift 是一門面向協(xié)議 (POP) 開發(fā)的語言
我說一下我的體會吧,我剛開始做 iOS 開發(fā)的時(shí)候使用 OC 來開發(fā)的,后來學(xué)習(xí)了 Swift,當(dāng)時(shí)也是有一搭沒一搭的學(xué),了解了一下 Swift 的基本語法,感覺還是很簡單的,因?yàn)楫?dāng)時(shí) Swift 也是剛出來,很不穩(wěn)定,1.0 到 2.0,甚至都不兼容,所以也就沒有選擇使用 Swift 來開發(fā)。
直到去年,也就是16 年,在 2.2 版本出來之后,感覺 Swift 還算比較穩(wěn)定了,才決定使用 Swift 來開發(fā)一個(gè)簡單的項(xiàng)目,當(dāng)時(shí)決定模仿一個(gè) app 來練手,也就是現(xiàn)在在我的 github 上的那個(gè)項(xiàng)目了。但是現(xiàn)在看來,那個(gè)項(xiàng)目寫的不是很好,雖然是使用 Swift 來開發(fā)的,但是并沒有按照 Swift 的標(biāo)準(zhǔn)來寫 Swift 的項(xiàng)目,反而是以 OC 的習(xí)慣來寫 Swift,也就是還是按照面向?qū)ο蟮乃枷雭韺?Swift,雖然也能寫出可以運(yùn)行的項(xiàng)目,但是面向?qū)ο蟮乃枷刖秃?Swift 的編程思想還是有本質(zhì)的區(qū)別的。這里我不想著重介紹關(guān)于 OC 這門編程語言,畢竟我們現(xiàn)在是用 Swift 來開發(fā)的,但是有些東西還是要說明一下,首先面向?qū)ο缶幊痰奶卣魇?class,繼承,封裝和多態(tài),其實(shí) OC 還不能說是一門純面向?qū)ο蟮牡恼Z言,只能說 OC 是 C 語言的超集,或者說是 C 語言的擴(kuò)展,在 C 語言的基礎(chǔ)上增加了面向?qū)ο蟮乃枷搿5窃?Swift 里就不一樣了,Swift 里 class 并不是最重要的。
我前面說了 Swift 是面向協(xié)議的編程,那么究竟什么是面向協(xié)議編程呢?
要回答這個(gè)問題,我們可以參考一下剛剛提到的面向?qū)ο缶幊蹋诿嫦驅(qū)ο缶幊汤?,是從一個(gè) class 開始的,那要是照這樣說,在面向協(xié)議編程里就是從一個(gè) protocol 了嗎?這樣解釋對不對呢?我們可以在剛剛提到視頻里找找答案,如果看過上面的視頻,你會發(fā)現(xiàn)在上面的視頻中 Apple 自己都說:
"從一個(gè) protocol 開始,別從 class 開始。" ——Dave Abrahams: 毀你三觀教授
protocol 就是協(xié)議的意思。當(dāng)然,可以從protocol 開始,但是從 protocol 開始了之后,該怎么做呢?
是的,這也是我們該思考的問題,我這里不會太著重去介紹 Swift 的基礎(chǔ),因?yàn)槲夷J(rèn)看我視頻的同學(xué)都已經(jīng)掌握了 Swift 的基礎(chǔ)了,所以關(guān)于 protocol 的概念我也不在詳細(xì)介紹了,回到我們剛才的問題,現(xiàn)在我們已經(jīng)有了 protocol,接下來我們要做的就是使用非常強(qiáng)大的 extension 了,額…,關(guān)于 extension 的概念我也不再詳細(xì)介紹了,如果感覺基礎(chǔ)不好的同學(xué)可以先去看一下基礎(chǔ),然后再來看我的視頻吧,關(guān)于 extension,可以為現(xiàn)有的 class,struct,enum,protocol 添加新功能,注意剛剛我提到了 protocol,所以我們先現(xiàn)在可以在 protocol 的extension 里添加任何你需要添加的東西了。
那好,功能也添加了,那怎么該怎么使用這個(gè) protocol 呢?
這也是個(gè)問題,讓我們再分析一下,protocol 不同于 class 或者 struct,因?yàn)楹髢烧呖梢愿髯哉{(diào)用它們的類型方法或者實(shí)例方法,但是 protocol 卻不能直接使用,也不能實(shí)例化,既然都不行,那該怎么做???別著急,既然不能直接用,那我們就要考慮用上面提到的 class 或者 struct 了,那我們該用哪個(gè)呢?我們先來看一張圖:
[圖片上傳失敗...(image-fd804b-1517807204933)]
這張圖是我在網(wǎng)上找到的一篇文章中的截圖,下面是文章地址: 不要用子類!Swift的核心是面向協(xié)議 ,雖然這篇文章是2015年的文章了,不過還是推薦大家看一下。在上面的圖中,可以看出在 Swift 的標(biāo)準(zhǔn)庫中,僅有 4 個(gè)class,其余下的有 87 個(gè) struct 和 8 個(gè) enum 的實(shí)例共同構(gòu)建了 Swift 功能的核心。如今已經(jīng)過去兩年,我想 struct 的數(shù)量應(yīng)該更多了。既然 Swift 里用了這么多 struct,為什么我們不試試用 struct 呢?
我們前面也說過了 class 是面向?qū)ο罄锏臇|西,那我們試試用 struct,現(xiàn)在可以新建一個(gè) struct,然后讓它遵守我們的 protocol 就可以了,之后就可以實(shí)例化一個(gè) struct,接著就可以用 struct 調(diào)用 protocol 里的方法或者屬性了。
聽上去還不是錯(cuò)的,但是總感覺是不是有點(diǎn)太麻煩了,要是按照上面說的,我們直接創(chuàng)建一個(gè) struct 不就完了嘛,還要 protocol 干什么,這么說聽上去也沒有問題,當(dāng)然在開發(fā)中也是可以的.
但是我們還要考慮一個(gè)問題,在實(shí)際開發(fā)中我們是不是只有 struct 呢?
當(dāng)然不是,因?yàn)槲覀冞€要和 cocoa 框架打交道,說到 cocoa 框架,我們還要提一下 UIKit 這個(gè)框架,這是 iOS 開發(fā)中一個(gè)十分重要的框架,但是由于歷史關(guān)系,為了兼容 OC,UIKit 里的類都是繼承自 NSObject 的,也就是說都是 class 類型的,比如在開發(fā)中有幾十個(gè)控制器都繼承自某個(gè)自定義的基類,就會把基類的所有的方法也繼承下來,但是這些方法對每一個(gè)子類都有用嗎?答案肯定是否定的。所以,既然子類不需要,何必要繼承父類的方法呢?自己的方法應(yīng)該由自己決定才對的,而現(xiàn)在是基類幫著子類決定了它的方法。
所以這樣就引出了 protocol,讓自己的類實(shí)現(xiàn)自己所要遵守的 protocol,這里我說的并不是某一個(gè) class,我這里指的是有那么幾個(gè) class 都要實(shí)現(xiàn)功能的時(shí)候,選擇用 protocol 是個(gè)不錯(cuò)的選擇,而且還可以把幾個(gè)方法抽象成一個(gè)方法,需要的 class 只需要遵守這個(gè) protocol 就可以了。這樣解釋可能不太清楚,我舉一個(gè)栗子。
當(dāng)我自定義 UIView 的時(shí)候,我想讓 view 從 xib 加載,那么我就需要在每個(gè)類里都寫一個(gè)從 xib 加載的類方法,如下代碼:
static func classMethodCreateView() -> MyCustomView {
return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.last as! MyCustomView
}
這樣在每個(gè)代碼都寫一,很是麻煩,有什么方法可以簡單一點(diǎn)嗎?方法當(dāng)然是有的,可以做一下優(yōu)化,如下代碼:
protocol LoadNibProtocol {}
extension LoadNibProtocol where Self: UIView {
/// 提供加載 Xib 方法
static func loadViewFromNib(name: String? = nil) -> Self {
return Bundle.main.loadNibNamed(name ?? "\(self)", owner: nil, options: nil)?.last as! Self
}
}
接下來讓需要從 xib 加載的 view 遵守 LoadNibProtocol 協(xié)議就可以了,是不是簡單了許多呢?
上面只是 protocol 的一個(gè)簡單應(yīng)用,在后面的項(xiàng)目中,我會介紹其他用法,這里就不再過多說明了,關(guān)于協(xié)議暫時(shí)先介紹這么多。
下面還有一個(gè)問題,需要思考一下,就是 Swift 里既然有 class 和 struct,那么他們的區(qū)別是什么呢?
我想大多數(shù)人的第一反應(yīng)應(yīng)該是 struct 是值類型 class 是引用類型,也就是說 struct 的實(shí)例在被賦予變量或者常量或者被函數(shù)調(diào)用時(shí)都會被復(fù)制,但是 class 的實(shí)例會被引用,引用的就是已經(jīng)存在的實(shí)例本身而不是復(fù)制。還可以這樣理解 struct 的復(fù)制相當(dāng)于在內(nèi)存上又開辟了一塊內(nèi)存空間,和之前的 struct 沒有關(guān)系了,我個(gè)人感覺也可以理解成深拷貝,而 class 則是創(chuàng)建一個(gè)指針,指向的還是原來的內(nèi)存地址,可以理解成淺拷貝。
class 可以繼承,struct 不能繼承,某些需要繼承的地方還是需要用 class,不能用 struct。
struct 類型方法要加 static修飾,class類型方法要加 class 修飾。
-
struct 有默認(rèn)的初始化方法,class 需要指定變量的初始值。
下面代碼關(guān)于 class 和 struct 的在初始化的時(shí)候的一些區(qū)別。
struct MyStruct1 {
var text: String
var tip: Int
}
struct MyStruct2 {
var text: String = "MyStruct2"
var tip: Int = 2
}
struct MyStruct3 {
var text: String
var tip: Int
init(text: String, tip: Int) {
self.text = text
self.tip = tip
}
}
class MyClass1 {
var text: String = "MyClass1"
var tip: Int = 11
}
class MyClass2 {
var text: String
var tip: Int
init() {
text = "MyClass2"
tip = 22
}
}
class MyClass3 {
var text: String
var tip: Int
init(text: String, tip: Int) {
self.text = text
self.tip = tip
}
}
let myStruct1 = MyStruct1(text: "MyStruct1", tip: 1)
let myStruct2 = MyStruct2()
let myStruct3 = MyStruct3(text: "MyStruct3", tip: 3)
let myClass1 = MyClass1()
let myClass2 = MyClass2()
let myClass3 = MyClass3(text: "MyClass3", tip: 33)
還有一點(diǎn),就是關(guān)于 struct 和 class 的性能差異,可以閱讀下面的文章:理解Swift中struct和class在不同情況下性能的差異,文章介紹的很詳細(xì),我這里也不再詳細(xì)介紹了。
上面是我對 struct 和 class 做的簡單說明,以及 Swift 面向協(xié)議編程的簡單說明,如果還覺得意猶未盡,或者想了解更多內(nèi)容,請自行去網(wǎng)上找找相關(guān)文章。
說了這么多,最后還是希望你們能明白 Swift 是面向協(xié)議的編程, 在開發(fā)過程中請多使用 struct 和 protocol,當(dāng)你沒有選擇的時(shí)候再使用 class。
新增一篇參考文章:面向協(xié)議的 MVVM 架構(gòu)介紹。這篇文章也比較早了。
下面我們就繼續(xù)寫代碼吧。
首先新建兩個(gè) Swift 文件,一個(gè)命名為 MyCellModel.Swift,作為我的界面 cell 的模型。
另一個(gè)命名為 NetworkTool.Swift,作為網(wǎng)絡(luò)請求的相關(guān)文件。
然后在 Podfile 添加我們需要的第三方框架,分別是 Alamofire,SwiftyJSON,HandyJSON。
如下代碼:
target 'News' do
use_frameworks!
pod 'Alamofire', '~> 4.5.0' # 數(shù)據(jù)請求 https://github.com/Alamofire/Alamofire,同 AFNetworking
pod 'HandyJSON', '~> 1.7.2' # JSON序列化/反序列化庫 https://github.com/alibaba/HandyJSON/
pod 'SwiftyJSON' # json 解析 https://github.com/SwiftyJSON/
end
默認(rèn)生成的測試 target 先不需要,可以刪掉。
關(guān)于上面的第三方框架可以去 github 看一下他們的介紹和用法,我這里就不詳細(xì)說明了,看我是怎么寫的就可以了,跟著我寫,寫著寫著就知道怎么用了。
我的博客即將搬運(yùn)同步至騰訊云+社區(qū),邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=nmhhdqxcpeov