本文算是我對Swift基礎知識的一個再梳理蝗岖,不會再討論基礎語法,而是把一些文檔之外的東西架曹,進行一次再梳理總結隘冲,方便查閱。
我們這里首先來做一個分析音瓷,一個是普通的String對象对嚼,一個是遵守協議的String對象夹抗,這里我們來對比一下它們各自占用多大的內存绳慎?
這里先羅列一下實驗的協議和擴展代碼:
protocol TestProtocol {
var testData:NSURL? {get}
}
extension String:TestProtocol{
var testData:NSURL? {
return NSURL()
}
}
然后我們開始書寫下面的測試代碼:
var qurl:TestProtocol = "123"
print(MemoryLayout<TestProtocol>.size) //40
var string:String = "123"
print(MemoryLayout<String>.size) //24
我們可以在playground中看到結果,遵守protocol的qurl的大小為40漠烧,而不遵守任何協議的string的大小為24.
在Swift中杏愤,由于string是stuct結構體,而不再是OC中的class已脓,所以不再是8個字節(jié)珊楼,24個字節(jié)的結構也可以用LLDB動態(tài)調試type lookup String
來查看
,最后我們發(fā)現
var _baseAddress: Swift.UnsafeMutableRawPointer?
var _countAndFlags: Swift.UInt
var _owner: AnyObject?
這三個屬性每個大小為8個字節(jié)度液,加起來就是24字節(jié)厕宗,那么協議對象的40字節(jié)又該如何解釋呢?
這里我們首先查看對象的地址堕担,這里我們首先寫一個打印地址的函數
func addrOf<T>( v:inout T){
withUnsafePointer(to: &v) { print($0)}
}
addrOf(v: &qurl)
最后我這里打印出結果已慢,地址為0x00000001003dfb20
然后我們實用LLDB的動態(tài)調試指令:x/5xg 0x00000001003dfb20
查看其5個字長的內存空間
得出結果
0x1003dfb20: 0x00000001003397f8 0x0000000000000003
0x1003dfb30: 0x0000000000000000 0x00000001003ad878
0x1003dfb40: 0x000000010038a548
這里繼續(xù)使用image lookup -a 0x00000001003397f8
查看第一個地址
我們可以發(fā)現值為
Address: SwiftTest[0x00000001003397f8] (SwiftTest.__TEXT.__cstring + 72)
Summary: "123"
這第一個值其實存放的是遵守協議的字符串的值,我們可以發(fā)現它是cstring
第二個存放字符個數和第三個地址為0霹购,這兩個我們不管佑惠,直接解析第4個和第5個地址的內容。
image lookup -a 0x00000001003ad878
Address: SwiftTest[0x00000001003ad878] (SwiftTest.__DATA.__const + 144264)
Summary: SwiftTest`type metadata for Swift.String
我們可以發(fā)現第四個地址存放的是type metadata,即類型元數據膜楷,類型元數據即描述類的數據旭咽,有點類似Objective-C中的元類的作用
image lookup -a 0x000000010038a548
Address: SwiftTest[0x000000010038a548] (SwiftTest.__DATA.__const + 88)
Summary: SwiftTest`protocol witness table for Swift.String : SwiftTest.TestProtocol in SwiftTest
而第5個地址打印出味protocol witness table,即協議見證表赌厅,這個概念非常類似與Cpp中的vtable穷绵,即虛函數表。
那么什么是虛函數表呢特愿?
在Swift请垛,不同的結構、枚舉洽议、類都可以繼承協議,同樣的url屬性就會產生不同的getter方法宗收。就像這張Cpp中的虛函數表一樣,不同vfunc1可能有不同的實現函數地址亚兄,所以需要有一個虛函數表來維護混稽。
這里我們繼續(xù)試驗:
我們發(fā)現第5個地址即指向虛函數表的地址,那個根據如圖所示审胚,其實我們可以繼續(xù)解析這個虛函數表的地址的內存匈勋,繼續(xù)使用
x/xg 0x000000010038a548
0x10038a548: 0x0000000100002470
我們再看看看看這個虛函數表存放的地址的指令
x/i 0x0000000100002470
0x100002470: 55 pushq %rbp
看到pushq %rbp
(這句表示:將調用函數的棧底壓棧到被調函數的棧中),我們就應該猜到這個地址存放了一個函數膳叨。
于是使用
image lookup -a 0x0000000100002470
Address: SwiftTest[0x0000000100002470] (SwiftTest.__TEXT.__text + 2608)
Summary: SwiftTest`protocol witness for SwiftTest.TestProtocol.testData.getter : Swift.Optional<__ObjC.NSURL> in conformance Swift.String : SwiftTest.TestProtocol in SwiftTest at TestDataConvertible.swift
我們就能發(fā)現在該支持實際存放了一個testData.getter方法洽洁。
如果有多個對象,那么就會變成這樣的結構:
通過這樣的結構菲嘴,也說明了為什么協議只能存儲計算屬性而不能存儲 存儲屬性饿自,這就是我對協議的理解,有更多關于協議的有趣內容龄坪,歡迎下方留言昭雌,與我分享。