類與結構體的對比
在 Swift 中類和結構體有很多共同之處
- 定義屬性用來存儲值鞠抑;
- 定義方法用于提供功能铺厨;
- 定義下標腳本用來允許使用下標語法訪問值入篮;
- 定義初始化器用于初始化狀態(tài)贾惦;
- 可以被擴展來默認所沒有的功能癣诱;
- 遵循協(xié)議來針對特定類型提供標準功能盹沈。
類有而結構體沒有的額外功能
- 繼承允許一個類繼承另一個類的特征;
- 類型轉換允許你在運行檢查和解釋一個類實例的類型龄章;
- 反初始化器允許一個類實例釋放任何其所被分配的資源;
- 引用計數(shù)允許不止一個對類實例的引用。
結構體
在swift標準庫中做裙,絕大多數(shù)的類型都是結構體岗憋,而枚舉和類只占一小部分。比如Bool锚贱、Int仔戈、Double、String拧廊、Array监徘、Dictionary
等常見的類型都是結構體
結構體初始化器
編譯器會根據(jù)情況,可能會為結構體生成多個初始化器卦绣,宗旨:保證所有的成員都有值
所有結構體都有一個編譯器自動生成的初始化器
思考:下面的代碼能夠通過編譯器嗎
struct Point {
var x:Int?
var y:Int?
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1)
var p3 = Point()
答案是可以的
可選項都有一個默認值nil
自定義初始化器
一旦在自定義結構體時自定義了初始化器耐量,編譯器就不會再幫它自動生成其他初始化器了
在沒有自定義初始化器的時候,下面三種情況都是可以通過編譯的
struct Point {
var x:Int = 0
var y:Int = 1
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1)
var p3 = Point()
而自定義了初始化器
struct Point {
var x:Int = 0
var y:Int = 1
init(x:Int,y:Int) {
self.x = x
self.y = y
}
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1)
var p3 = Point()
類
類的定義和結構體相似滤港,但是編譯器并沒有為類自動生成可以傳入成員值的初始化器
類和結構體的本質區(qū)別
- 結構體和枚舉是值類型:值類型是一種當它被指定到常量或者變量廊蜒,或者被傳遞給函數(shù)時會被拷貝的類型。
- 類是引用類型:不同于值類型溅漾,在引用類型被賦值到一個常量山叮,變量或者本身被傳遞到一個函數(shù)的時候它是不會被拷貝的。相對于拷貝添履,這里使用的是同一個對現(xiàn)存實例的引用屁倔。
用結構體和元組構建更整潔的類
假設你正在開發(fā)一款社交網(wǎng)絡應用,其中包含了一個帶有關注按鈕和點贊按鈕的用戶圖片展示組件暮胧。同時锐借,為了滿足單一功能原則(single responsibility principle)和視圖控制器的構成,點贊和關注的實現(xiàn)應該另有它處往衷。社交網(wǎng)絡不僅有高級賬戶钞翔,也有企業(yè)賬戶,因此 InteractiveUserImageController(命名從來不是我的強項) 要能滿足一系列的配置選項席舍。以下是這個類一個可能的實現(xiàn)(為作展示布轿,示例代碼保留了不少可改進的地方):
final class InteractiveUserImageController: UIView {
/// 是否需要展示高級布局
var isPremium: Bool
/// 賬戶類型
var accountType: AccountType
/// 點擊視圖是否高亮
var isHighlighted: Bool
/// 用戶名
var username: String
/// 用戶頭像
var profileImage: UIImage
/// 當前用戶是否能點贊該用戶
var canLike: Bool
/// 當前用戶是否能關注該用戶
var canFollow: Bool
/// 大贊按鈕是否能使用
var bigLikeButton: Bool
/// 針對一些內容使用特殊的背景色
var alternativeBackgroundColor: Bool
init(...) {}
}
至此,我們就有了不少參數(shù)来颤。隨著應用體量的增長汰扭,會有更多的參數(shù)被加進類里。將這些參數(shù)通過職能進行劃分和重構固然可行福铅,但有時保持了單一功能后仍會有大量的參數(shù)存在萝毛。要如何才能更好的組織代碼呢?
Swift 結構體結構
Swift 的struct
類型在這種情況能發(fā)揮巨大的作用滑黔。依據(jù)參數(shù)的類型將它們裝進一次性結構體:
final class InteractiveUserImageController: UIView {
struct DisplayOptions {
/// 大贊按鈕是否能使用
var bigLikeButton: Bool
/// 針對一些內容使用特殊的背景色
var alternativeBackgroundColor: Bool
/// 是否需要展示高級布局
var isPremium: Bool
}
struct UserOptions {
/// 賬戶類型
var accountType: AccountType
/// 用戶名
var username: String
/// 用戶頭像
var profileImage: UIImage
}
struct State {
/// 點擊視圖是否高亮
var isHighlighted: Bool
/// 當前用戶是否能點贊該用戶
var canLike: Bool
/// 當前用戶是否能關注該用戶
var canFollow: Bool
}
var displayOptions = DisplayOptions(...)
var userOptions = UserOptions(...)
var state = State(...)
init(...) {}
}
正如你所見珊泳,我們把這些狀態(tài)放入了獨立的 struct
類型中鲁冯。不僅讓類更整潔,也便于新上手的開發(fā)者找到相關聯(lián)的選項色查。
已經(jīng)是一個不錯的改進了,但我們能做得更好撞芍!
我們面臨的問題是查找一個參數(shù)需要額外的操作秧了。
由于使用了一次性結構體類型,我們需要在某處定義它們(例如:struct DisplayOptions
)序无,也需要將它們實例化(例如:let displayOptions = DisplayOptions(...)
)验毡。大體上來說沒什么問題,但在更大的類中帝嗡,為確定 displayOptions 的類型仍舊需要一次額外的查詢晶通。然而,與 C 語言不同哟玷,像下面這樣的匿名struct
在 Swift 里并不存在:
let displayOptions = struct {
/// 大贊按鈕是否能使用
var bigLikeButton: Bool
/// 針對一些內容使用特殊的背景色
var alternativeBackgroundColor: Bool
/// 是否需要展示高級布局
var isPremium: Bool
}
元組 – 匿名結構體在 Swift 中的實現(xiàn)
實際上狮辽,Swift 中還真有這么一個類型。它就是我們的老朋友巢寡,tuple喉脖。自己看吧:
var displayOptions: (
bigLikeButton: Bool,
alternativeBackgroundColor: Bool,
isPremium: Bool
)
這里定義了一個新的類型 displayOptions
,帶有三個參數(shù)(bigLikeButton抑月,alternativeBackgroundColor树叽,isPremium
),它能像前面的 struct 一樣被訪問:
user.displayOptions.alternativeBackgroundColor = true
更好的是谦絮,參數(shù)定義不需要做額外的初始化题诵,一切都井然有序。
強制不可變性
最后层皱,tuple
既可以是 可變的
也可以是 不可變的
性锭。正如你在第一行所看到的那樣:我們定義的是var displayOptions
而不是 var
或 let bigLikeButton
。bigLikeButton和
displayOptions一樣也是
var`奶甘。這樣做的好處在于強制把靜態(tài)常量(例如行高篷店,頭部高度)放入一個不同的(let)組。
添加數(shù)據(jù)
當需要用一些值初始化參數(shù)時臭家,你也能很好的利用這個特性疲陕,這是一個加分項:
var displayOptions = (
bigLikeButton: true,
alternativeBackgroundColor: false,
isPremium: false,
defaultUsername: "Anonymous"
)
簡化
相比于使用結構體而言,使用了元組的選項集能更輕易的簡化代碼:
class UserFollowComponent {
var displayOptions = (
likeButton: (
bigButton: true,
alternativeBackgroundColor: true
),
imageView: (
highlightLineWidth: 2.0,
defaultColor: "#33854"
)
)
}