Swift的struct設計理念 - 簡單又可靠

city.jpg
Struct概述
Swift語言有兩種基本的資料型別,即類(class)和結構體(struct),class這樣的概念大家不會陌生,而struct也並不是什麼新的概念,在Objective-C和C++也有struct,不過swift將struct提升到一個更高更重要的層次,甚至swift Foundation框架的SDK,諸如String,Array,Dictionary都是基於struct實現的。
筆者剛開始接觸swift時,認為struct是一個附屬品,然而隨著開發的深入和閱讀程式碼量的上升,發現struct的使用場景很多,而且很好用。
那麼struct與class相比,有什麼區別呢?主要的區別就在於class是型別引用,而struct是值引用,在Objective-C時代,我們對型別引用和值引用就有了一定的瞭解,例如在Objective-C中常用的NSArray, NSDictionary, NSString, UIKit等都是型別引用;而NSInteger, CGFloat, CGRect等則是值引用。顯然,在Objective-C中,引用型別佔據了很大的比重,現在使用swift開發應用程式,開發者需要轉變觀念,因為struct在swift變得越來越重要,觀念的轉變不僅在於多使用struct,還要求開發者理解struct的原理,優點及缺點。
在swift中,型別引用和值引用的區別在於,對於型別引用(class reference),將變數a賦值給變數b,即 b = a
,這樣的賦值語句僅僅將b的指標與a的指標一樣,指向同一塊記憶體區域,此時改變b的值,a也會跟著改變;而對於值引用(value reference),賦值語句 b = a
處理的過程是開闢一個新的記憶體b,將a變數的內容拷貝後存放到記憶體b,這時a和b完全沒有關係的兩個變數,對b的改變不會影響到a,反之亦然。
下面運行於Xcode playground的demo說明了class的型別引用和struct的值引用的區別,
Swift class型別引用(class reference)的demo展示
class SomeClass { var name: String? init(name: String) { self.name = name } } var aClass = SomeClass(name: "dante") var bClass = aClass //此時bClass和aClass指向同一個記憶體區域 // 改變bClass的name值 bClass.name = "flion" print(aClass.name) // "flion" print(bClass.name) // "flion"
此處的print結果是Optional值,"flion"是預設已經解包Optional變數,簡寫是為了demo說明,下面的demo也一樣,不再贅述。
上述程式碼首先定義了SomeClass以及-init初始化方法,然後建立name值為"dante"的aClass,接著將aClass賦值給bClass,此時改變bClass的name值為"flion",aClass的name值也跟著改變,這證明了型別引用的賦值操作實際將兩個變數的指標指向了同一塊區域。
用圖簡單說明,

reference-assignment.png
Swift struct值引用(value reference)的demo展示
struct SomeStruct { var name: String? init(name: String) { self.name = name } } var aStruct = SomeStruct(name: "dante") var bStruct = aStruct bStruct.name = "flion" print(aStruct.name) // "dante" print(bStruct.name) // "flion"
這裡定義了struct SomeStruct,建立name值為"dante"的aStruct變數,將aStruct賦值給bStruct變數,改變bStruct的name值為"flion",此時aStruct的name仍然是"dante",並沒有改變,這是因為aStruct和bStruct是兩塊不同的記憶體,兩者之間並沒有聯絡。
值引用賦值過程,如圖所示,

value-assignment.png
為什麼需要struct?
struct和class的主要區別,
- struct是值引用,而class是型別引用
- struct沒有繼承的功能,class有繼承功能
struct和class這兩個基本層面的區別,體現了區別於Objective-C語言,swift語言帶來了全新的天翻地覆的改變。
首先說第一點區別,從swift的更新和struct不斷完善來看,蘋果公司更加推薦使用struct來代替class,因為 struct值引用
和 class型別引用
這點區別,保證使用struct編碼能寫出更加安全可靠的程式碼。為什麼這樣說呢,class型別引用在賦值時是將變數指向了同一塊記憶體地址,這在一個長時間的跨度上會帶來一些意想不到的問題,試想一個簡單的例子,viewControllerA持有一個NSMutableArray陣列mutalbeArray,它包含100條user資訊,此時將mutableArray賦值給viewControllerB,對於viewControllerB而言,它僅僅需要前10條user資訊,所以它將mutableArray多餘的資訊刪除了,這樣一個腦殘的操作導致了viewControllerA模組展示錯誤和潛在的邏輯錯誤。而使用struct值引用則不會出現這樣的問題。
第二點區別,struct沒有繼承的功能,這是因為swift在本質上來說是面向協議(Protocol Oriented)的語言,struct沒有也不需要繼承的功能,為了實現某個功能,struct去服從並實現某個協議就即可,從一個較高的層次來看,struct+protocol是構成swift面向協議語言的兩個基石。這一點不在本文討論範圍,不再贅述。
為什麼要使用struct呢?總結就是struct可以保證程式碼更加安全可靠,以及struct+protocol更加切合swift面向協議程式設計的初衷。
struct基本語法
和class一樣,struct也可以定義屬性和方法,同樣struct也要求完整初始化,即保證初始化過程中每一個non-optional屬性要賦予明確的初始值,如下程式碼定義了Person struct,
struct Person { var firstName: String var secondName: String? // optional value var salary: Int // default designed init - 預設的指定初始化方法 init() { firstName = "flion" salary = 100 } // 帶引數的指定初始化 init(firstName: String, secondName: String?, salary: Int) { self.firstName = firstName self.secondName = secondName self.salary = salary } func eat(foodName: String) -> Void { print("eat food " + foodName) } func fullName() -> String { if let tempSecondName = secondName { return firstName + "-" + tempSecondName } else { return firstName } } } let flion = Person(firstName: "flion", secondName: "dep", salary: 100) print(flion.fullName())
swift的class -init初始化方法有很多的規範和要求,對於初學者來說這是一個很大的難度和挑戰,這不在本篇文章的討論範圍,讀者可參考蘋果官方文件Swift Init檢視更多內容,在此不再贅述。
與class的-init初始化方法不同的是,struct的-init並沒有便利初始化(convenience init)方法;又因為struct沒有繼承,所以struct的-init也不需要required關鍵字。這樣對比,struct的-init初始化方法比起class的-init初始化方法來說要簡單的多,因為struct只有designed init即指定初始化, struct只需要保證指定初始化過程中每個非可選屬性都賦值 ,沒有複雜的-init初始化規則和規範,struct相比於class也顯得更加 簡單 ,更有親和力。
筆者在struct init嘗試使用convenicence和requeired關鍵字時候,Xcode提示編譯錯誤,所以根據觀察現象和思維分析作的這樣一個結論,並沒有參考官方文件或者經過嚴格的驗證,如果這個說法有錯誤,請幫我指正,thank U。
參考連結
- ofollow,noindex">http://stackoverflow.com/questions/24217586/structure-vs-class-in-swift-language
公眾號
歡迎關注本人公眾號 foolishlion,請掃描下方二維碼,

foolishlion.jpg