Swift中熟悉的陌生人:Protocol(一)
本文算是我對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個位元組,24個位元組的結構也可以用LLDB動態除錯type lookup String
來檢視
,最後我們發現
var _baseAddress: Swift.UnsafeMutableRawPointer?
var _countAndFlags: Swift.UInt
var _owner: AnyObject?
這三個屬性每個大小為8個位元組,加起來就是24位元組,那麼協議物件的40位元組又該如何解釋呢?
這裡我們首先檢視物件的地址,這裡我們首先寫一個列印地址的函式
func addrOf<T>( v:inout T){
withUnsafePointer(to: &v) { print($0)}
}
addrOf(v: &qurl)
最後我這裡列印出結果,地址為0x00000001003dfb20
然後我們實用LLDB的動態除錯指令:x/5xg 0x00000001003dfb20
檢視其5個字長的記憶體空間
得出結果
0x1003dfb20: 0x00000001003397f8 0x0000000000000003
0x1003dfb30: 0x0000000000000000 0x00000001003ad878
0x1003dfb40: 0x000000010038a548
這裡繼續使用image lookup -a 0x00000001003397f8
檢視第一個地址
我們可以發現值為
Address: SwiftTest[0x00000001003397f8] (SwiftTest.__TEXT.__cstring + 72)
Summary: "123"
這第一個值其實存放的是遵守協議的字串的值,我們可以發現它是cstring
第二個存放字元個數和第三個地址為0,這兩個我們不管,直接解析第4個和第5個地址的內容。
image lookup -a 0x00000001003ad878
Address: SwiftTest[0x00000001003ad878] (SwiftTest.__DATA.__const + 144264)
Summary: SwiftTest`type metadata for Swift.String
我們可以發現第四個地址存放的是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可能有不同的實現函式地址,所以需要有一個虛擬函式表來維護。
這裡我們繼續試驗:
我們發現第5個地址即指向虛擬函式表的地址,那個根據如圖所示,其實我們可以繼續解析這個虛擬函式表的地址的記憶體,繼續使用
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
我們就能發現在該支援實際存放了一個testData.getter方法。
如果有多個物件,那麼就會變成這樣的結構:
通過這樣的結構,也說明了為什麼協議只能儲存計算屬性而不能儲存 儲存屬性,這就是我對協議的理解,有更多關於協議的有趣內容,歡迎下方留言,與我分享。
相關文章
- 說說Flutter中最熟悉的陌生人 —— KeyFlutter
- 構造最大數;及熟悉的陌生人分析
- 熟悉的陌生人;及又見Google搜尋之星分析Go
- Swift -- enum 繼承 protocolSwift繼承Protocol
- Swift - 使用 Protocol 避免框架之間迴圈引用SwiftProtocol框架
- Dubbo on x-protocol——SOFAMesh中的x-protocol示例演示Protocol
- 翻譯:Swift 5.1中的Protocol面向協議的程式設計教程:從入門到精通SwiftProtocol協議程式設計
- Protocol Buffers 在 iOS 中的使用ProtocoliOS
- swift 中的 ??Swift
- Taro 小程式開發大型實戰(一):熟悉的 React,熟悉的 HooksReactHook
- Swift Protocol 詳解 - 協議&面向協議程式設計SwiftProtocol協議程式設計
- Swift代理報錯Optional can only be applied to members of an @objc protocolSwiftAPPOBJProtocol
- Perl 6 中的 Roles Vs. ProtocolProtocol
- 一加5T釋出!還是熟悉的外形,熟悉的內在?
- Swift 中的 @autoclosureSwift
- Swift中的反射Swift反射
- swift 中的 lazySwift
- swift 中的 whereSwift
- 窺探Swift之協議(Protocol)和委託代理(Delegate)回撥的使用Swift協議Protocol
- 關於Swift中Properties的一些理解Swift
- Swift中Initialization的一些個人總結Swift
- 熟悉shell的一些基本命令
- Swift 4 中的字串Swift字串
- Swift中的模式匹配Swift模式
- Swift 中的屬性Swift
- Swift中的協議Swift協議
- swift 中的 @UIApplicationMainSwiftUIAPPAI
- swift 中的 SelectorSwift
- Swift中的SelectorSwift
- Swift 中的 RuntimeSwift
- 也談Hypertext Transfer Protocol中Transfer的譯法Protocol
- Swift 4 中的泛型Swift泛型
- Swift 中的訪問控制Swift
- swift 中的 AnyObject 和 AnySwiftObject
- Swift3中的 GCDSwiftGC
- Swift中的奇淫巧技Swift
- Swift中的RactiveCocoa (上)Swift
- swift中 Selector的使用Swift