精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一篇帶給你Swift — 協議(Protocol)

開發 前端
協議定義了一個藍圖,規定了用來實現某一特定任務或者功能的方法、屬性,以及其他需要的東西。類、結構體和枚舉都可以遵循協議,并為協議定義的這些要求提供具體實現

[[392995]]

1. 前言

協議定義了一個藍圖,規定了用來實現某一特定任務或者功能的方法、屬性,以及其他需要的東西。類、結構體和枚舉都可以遵循協議,并為協議定義的這些要求提供具體實現。某個類型能夠滿足某個協議的要求,就可以說該類型遵循這個協議。

除了遵循協議的類型必須實現的要求外,還可以對協議進行擴展,通過擴展來實現一部分要求或者實現一些附加功能,這些遵循協議的類型就能夠使用這些功能。

2. 協議的基本用法

▐ 2.1 協議語法

協議的定義方式與類、結構體和枚舉的定義非常相似

1、基本語法

  1. protocol SomeProtocol { 
  2.     // 這里是協議的定義部分 

2、如果讓自定義的類型遵循某個協議,在定義類型時,需要在類型名稱后面加上協議名稱,中間以冒號(:)隔開,如果需要遵循多個協議時,個協議之間用逗號(,)分割:

  1. struct SomeStructure: FirstProtocol, AnotherProtocol { 
  2.     // 這里是結構體的定義部分 

3、如果自定義類型擁有一個父類,應該將父類名放在遵循協議名之前,以逗號分隔:

  1. class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { 
  2.     // 這里是類的定義部分 

▐ 2.2 屬性要求

我們可以在協議中添加屬性,但需要注意以下幾點:

  1. 屬性可以是實例屬性和類型屬性
  2. 屬性需要使用 var 修飾,不能屬于 let
  3. 類型屬性只能使用 static 修飾,不能使用 class
  4. 我們需要聲明屬性必須是可讀的或者可讀可寫的
  1. protocol SomeProtocol { 
  2.     var propertyOne: Int { get set } 
  3.     var propertyTwo: Int { get } 
  4.     static var propertyThree: Int { get set } 

▐ 2.3 方法要求

我們可以在協議中添加方法,但需要注意以下幾點:

  1. 可以是實例方法或類方法
  2. 像普通方法一樣放在協議定義中,但不需要大括號和方法體
  3. 協議中不支持為協議中的方法提供默認參數
  4. 協議中的類方法也只能使用 static 關鍵字作為前綴,不能使用 class
  5. 可以使用 mutating 提供異變方法,以使用該方法時修改實體的屬性等
  6. 可以定義構造方法,但是使用的時候需要使用 required 關鍵字
  1. protocol SomeProtocol { 
  2.     func someMethod1() 
  3.     func someMethod2() ->Int 

構造方法

  1. protocol SomeProtocol { 
  2.     init(param: Int
  3.  
  4. class SomeClass: SomeProtocol { 
  5.     required init(param: Int) { } 

異變方法

  1. protocol Togglable { 
  2.     mutating func toggle() 
  3.  
  4. enum OnOffSwitch: Togglable { 
  5.     case offon 
  6.     mutating func toggle() { 
  7.         switch self { 
  8.         case .off
  9.             self = .on 
  10.         case .on
  11.             self = .off 
  12.         } 
  13.     } 

▐ 2.4 協議作為類型

盡管協議本身并未實現任何功能,但是協議可以被當做一個功能完備的類型來使用。協議作為類型使用,有時被稱作「存在類型」,這個名詞來著存在著一個類型T,該類型遵循協議T。

協議可以像其他普通類型一樣使用,使用場景如下:

  • 作為函數、方法或構造器中的參數類型或返回值類型
  • 作為常量、變量或屬性的類型
  • 作為數組、字典或其他容器中的元素類型
  1. protocol SomeProtocol { } 
  2.  
  3. class SomeClass { 
  4.     required init(param: SomeProtocol) {} 

▐ 2.5 其他

  • 協議還可以被繼承
  • 可以在擴展里面遵循協議
  • 在擴展里面聲明采納協議
  • 使用合成來采納協議
  • 可以定義由類專屬協議,只需要繼承自AnyObject
  • 協議可以合成
  • 協議也可以擴展

3. 協議中方法的調用

舉個例子,在數學中我們會求某個圖形的面積,但是不同形狀求面積的公式是不一樣的,如果用代碼來實現可以怎么來實現呢?

首先我們可以通過繼承父類的方法來實現,但是在這里我們就可以使用協議來實現:

  1. protocol Shape { 
  2.     var area: Double {get} 
  3.  
  4. class Circle: Shape{ 
  5.     var radius: Double 
  6.     
  7.     init(_ radius: Double) { 
  8.         self.radius = radius 
  9.     } 
  10.      
  11.     var area: Double
  12.         get{ 
  13.             return radius * radius * 3.14 
  14.         } 
  15.     } 
  16. class Rectangle: Shape{ 
  17.     var width, height: Double 
  18.     init(_ width: Double, _ height: Double) { 
  19.         self.width = width 
  20.         self.height = height 
  21.     } 
  22.      
  23.     var area: Double
  24.         get{ 
  25.             return width * height 
  26.         } 
  27.     } 
  28.  
  29. var circle: Shape = Circle.init(10.0) 
  30. var rectangle: Shape = Rectangle.init(10.0, 20.0) 
  31.  
  32. print(circle.area) 
  33. print(rectangle.area) 
  34.  
  35. <!--打印結果--> 
  36. 314.0 
  37. 200.0 

此時的打印結果是符合我們的預期的。

我們知道協議可以擴展,此時我們把協議的代碼修改成如下:

  1. protocol Shape { 
  2. //    var area: Double {get} 
  3. extension Shape{ 
  4.     var area: Double { 
  5.         get{return 0.0} 
  6.     } 
  7.  
  8. <!--打印結果--> 
  9. 0.0 
  10. 0.0 

此時并沒有如我們預期的打印,如果我們聲明變量的時候寫成如下呢:

  1. var circle: Circle = Circle.init(10.0) 
  2. var rectangle: Rectangle = Rectangle.init(10.0, 20.0) 
  3.  
  4. <!--打印結果--> 
  5. 314.0 
  6. 200.0 

此時的打印就符合我們的預期了。

其實我們也能夠清楚的了解到為什么會打印 0.0,在 Swift 方法調度這篇文章中我們介紹了 extension 中聲明的方法是靜態調用的,也就是說在編譯后當前代碼的地址已經確定,我們無法修改,當聲明為 Shap 類型后,默認調用的就是 Shape extension 中的屬性的 get 方法。下面我們在通過sil代碼來驗證一下,關于生成 sil 代碼的方法,請參考我以前的文章。

為了方便查看,我們精簡并修改代碼為如下:

  1. protocol Shape {//    var area: Double {get}}extension Shape{    var area: Double {        get{return 0.0}    }}class Circle: Shape{    var radius: Double       init(_ radius: Double) {        self.radius = radius    }        var area: Double{        get{            return radius * radius * 3.14        }    }}var circle: Shape = Circle.init(10.0)var a = circle.area 

生成的 sil 代碼:

通過 sil 代碼我們可以清晰的看到,這里直接調用的 Shape.area.getter 方法。

下面我們換一些簡單的代碼再次看一下:

  1. protocol PersonProtocol { 
  2.     func eat() 
  3. extension PersonProtocol{ 
  4.     func eat(){ print("PersonProtocol eat") } 
  5. class Person: PersonProtocol{ 
  6.     func eat(){ print("Person eat") } 
  7. let p: PersonProtocol = Person() 
  8. p.eat() 
  9. let p1: Person = Person() 
  10. p1.eat() 
  11.  
  12. <!--打印結果--> 
  13. Person eat 
  14. Person eat 

可以看到上面這段代碼的打印結果都是 Person eat,那么為什么會打印相同的結果呢?首先通過代碼我們可以知道,在PersonProtocol中聲明了eat方法。對于聲明的協議方法,如果類中也實現了,就不會調用協議擴展中的方法。上面的屬性的例子中并沒有在協議中聲明屬性,只是在協議擴展中添加了一個屬性。下面我們看看上面這段代碼的sil代碼:

首先我們可以看到,對于兩個 eat 方法的確實存在不同,首先聲明為協議類型的變量調用 eat 方法是通過 witness_method 調用,另一個則是通過 class_method調用。

  • witness_method是通過PWT(協議目擊表)獲取對應的函數地址
  • class_method是通過類的函數表來查找函數進行調用

在剛剛 sil 代碼中我們可以找到 sil_witness_table,在里面有 PersonProtocol.eat方法,找到 PersonProtocol.eat 方法可以發現里面是調用 class_method 尋找的類中 VTable 的 Person.eat方法。

如果我們不在協議中聲明 eat 方法:

  1. protocol PersonProtocol { 
  2. //    func eat() 
  3. extension PersonProtocol{ 
  4.     func eat(){ print("PersonProtocol eat") } 
  5. class Person: PersonProtocol{ 
  6.     func eat(){ print("Person eat") } 
  7. let p: PersonProtocol = Person() 
  8. p.eat() 
  9. let p1: Person = Person() 
  10. p1.eat() 
  11.  
  12. <!--打印結果--> 
  13. PersonProtocol eat 
  14. Person eat 

查看 sil 代碼:

此時我們可以看到,對于不在協議中聲明方法的時候,依然是直接調用(靜態調用)。

所以對于協議中方法的調度:

對于不在協議中聲明的方法

  • 在協議擴展中有實現就是直接調用
  • 在遵循協議的實體中按照其調度方式決定
  • 兩處都實現了,聲明的實例是協議類型則直接調用協議擴展中的方法,反之調用遵循協議實體中的方法

對于聲明在協議中的方法

  • 如果遵循該協議的實體實現了該方法,則通過PWT協議目擊表查找到實現的方法進行調用(與聲明變量的類型無關)
  • 如果遵循協議的實體沒實現,協議擴展實現了,則會調用協議擴展中的方法

4. 協議原理探索

在上面探索協議中的方法調用的時候,我們提到過 PWT 也就是 Protocol witness table,協議目擊表,那么它存儲在什么地方呢?我們在 Swift 方法調度這篇文章中講過,V-Table 是存儲在 metadata 中的,那么我們就探索一下 PWT 的存儲位置。

▐ 4.1 內存占用

首先我們先來看看如下代碼的的打印結果:

  1. protocol Shape { 
  2.     var area: Double { get } 
  3. class Circle: Shape { 
  4.     var radius: Double 
  5.  
  6.     init(_ radius: Double) { 
  7.         self.radius = radius 
  8.     } 
  9.  
  10.     var area: Double
  11.         get{ return radius * radius * 3.14 } 
  12.     } 
  13.  
  14. var circle: Shape = Circle(10.0) 
  15. print(MemoryLayout.size(ofValue: circle)) 
  16. print(MemoryLayout.stride(ofValue: circle)) 
  17.  
  18. var circle1: Circle = Circle(10.0) 
  19. print(MemoryLayout.size(ofValue: circle1)) 
  20. print(MemoryLayout.stride(ofValue: circle1)) 
  21.  
  22. <!--打印結果--> 
  23. 40 
  24. 40 

▐ 4.2 lldb探索內存結構

看到這個打印結果我能第一時間想到的就是生命為協議類型會存儲更多的信息。生命為類的時候,存儲的是類的實例對象的指針 8 字節。下面我們通過 lldb 調試來探索一下這個 40 字節都存儲了什么信息。

▐ 4.3 sil 探索內存結構

通過 lldb 我們可以看到其內部應該存儲著一些信息,那么具體存了什么呢?我們在看看 sil 代碼:

在sil代碼中我們可以看到,在初始化 circle 這個變量的時候使用到了 init_existential_addr,查看SIL文檔:

譯文:用一個準備好包含類型為 $T 的存在容器部分初始化 %0 引用的內存。該指令的結果是一個地址,該地址引用了所包含值的存儲空間,該存儲空間仍然沒有初始化。包含的值必須存儲為 -d 或 copy_addr-ed,以便完全初始化存在值。如果存在容器的值未初始化時需要銷毀,則必須使用 deinit_existential_addr 來完成此操作。可以像往常一樣使用 destroy_addr 銷毀完全初始化的存在性容器。銷毀一個部分初始化存在容器的addr是未定義的行為。

文檔中的意思是,使用了包含 $T 的 existential container 來初始化 %0 引用的內存。在這里就是使用包含 Circle 的 existential container 來初始化 circle 引用的內存,簡單來說就是將 circle 包裝到了一個 existential container 初始化的內存。

existential container 是編譯器生成的一種特殊的數據類型,也用于管理遵守了相同協議的協議類型。因為這些塑化劑類型的內存空間尺寸不同,使用 existential container 進行管理可以實現存儲一致性。

▐ 4.4 IR代碼探索內存結構

那么這個 existential container 都包裝了什么呢?目前通過sil代碼是看不出來什么了,那么我們就看看 IR 代碼:

  1. ; 一個結構體,占用24字節內存的數組,wift.type指針, i8*指針 
  2. %T4main5ShapeP = type { [24 x i8], %swift.type*, i8** } 
  3.  
  4. define i32 @main(i32 %0, i8** %1) #0 { 
  5. entry: 
  6.   %2 = bitcast i8** %1 to i8* 
  7.   ; main.Circle 的 metadata 
  8.   %3 = call swiftcc %swift.metadata_response @"type metadata accessor for main.Circle"(i64 0) #7 
  9.   %4 = extractvalue %swift.metadata_response %3, 0 
  10.   ;init放 
  11.   %5 = call swiftcc %T4main6CircleC* @"main.Circle.__allocating_init(Swift.Double) -> main.Circle"(double 1.000000e+01, %swift.type* swiftself %4) 
  12.   ; 存%4 也就是metadata,存到T4main5ShapeP結構體中,這里存的位置是第二個位置 
  13.   store %swift.type* %4, %swift.type** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.circle : main.Shape", i32 0, i32 1), align 8 
  14.   ; 存pwt 也就是協議目擊表,存到第三個位置 
  15.   store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"protocol witness table for main.Circle : main.Shape in main", i32 0, i32 0), i8*** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.circle : main.Shape", i32 0, i32 2), align 8 
  16.   ; 存放%5到二級指針,%5是init出來的對象,所以這里也就是個HeapObject結構,也就是T4main6CircleC結構體的第一個8字節內存空間處 
  17.   store %T4main6CircleC* %5, %T4main6CircleC** bitcast (%T4main5ShapeP* @"main.circle : main.Shape" to %T4main6CircleC**), align 8 

從 IR 代碼中我們可以知道,這里面的存儲是一個結構體,結構體中主要分為三個方面:

  1. 一個連續的24字節空間
  2. 一個存放metadata的指針
  3. 存放pwt指針

▐ 4.5 仿寫

下面我們就來仿寫一下這個結構:

  1. struct HeapObject { 
  2.     var type: UnsafeRawPointer 
  3.     var refCount1: UInt32 
  4.     var refCount2: UInt32 
  5.  
  6. struct protocolData { 
  7.     //24 * i8 :因為是8字節讀取,所以寫成3個指針 
  8.     var value1: UnsafeRawPointer 
  9.     var value2: UnsafeRawPointer 
  10.     var value3: UnsafeRawPointer 
  11.     //type 存放metadata,目的是為了找到Value Witness Table 值目錄表 
  12.     var type: UnsafeRawPointer 
  13.     // i8* 存放pwt指針 
  14.     var pwt: UnsafeRawPointer 

4.5.1 類遵循協議重綁定

進行內存的重新綁定:

  1. protocol Shape { 
  2.     var area: Double { get } 
  3. class Circle: Shape { 
  4.     var radius: Double 
  5.  
  6.     init(_ radius: Double) { 
  7.         self.radius = radius 
  8.     } 
  9.  
  10.     var area: Double
  11.         get{ return radius * radius * 3.14 } 
  12.     } 
  13.  
  14. var circle: Shape = Circle(10.0) 
  15.  
  16. // 將circle強轉為protocolData結構體 
  17. withUnsafePointer(to: &circle) { ptr in 
  18.     ptr.withMemoryRebound(to: protocolData.self, capacity: 1) { pointer in 
  19.         print(pointer.pointee) 
  20.     } 
  21.  
  22. <!--打印結果--> 
  23. protocolData(value1: 0x00000001006082b0, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x0000000100008180, pwt: 0x0000000100004028) 

lldb

通過lldb查看:

我們也可以看到對應HeapObject結構

該結構存儲的是Circle的實例變量

并且在這里面的metadata與protocolData里面的存儲的metadata的地址是一致的;

通過cat address命令查看pwt對應的指針,可以看到這段內存對應的就是SwiftProtocol.Circle的protocol witness table。

至此我們就清楚的找到你了PWT的存儲位置,PWT存在協議類型實例的內存結構中。

4.5.2 結構體遵循協議重綁定

在上面這個例子中我們使用的是類,我們知道類是引用類型,如果換成結構體呢?

  1. protocol Shape { 
  2.     var area: Double {get} 
  3. struct Rectangle: Shape{ 
  4.     var width, height: Double 
  5.     init(_ width: Double, _ height: Double) { 
  6.         self.width = width 
  7.         self.height = height 
  8.     } 
  9.  
  10.     var area: Double
  11.         get{ 
  12.             return width * height 
  13.         } 
  14.     } 
  15.  
  16. var rectangle: Shape = Rectangle(10.0, 20.0) 
  17.  
  18.  
  19. struct HeapObject { 
  20.     var type: UnsafeRawPointer 
  21.     var refCount1: UInt32 
  22.     var refCount2: UInt32 
  23.  
  24.  
  25. struct protocolData { 
  26.     //24 * i8 :因為是8字節讀取,所以寫成3個指針 
  27.     var value1: UnsafeRawPointer 
  28.     var value2: UnsafeRawPointer 
  29.     var value3: UnsafeRawPointer 
  30.     //type 存放metadata,目的是為了找到Value Witness Table 值目錄表 
  31.     var type: UnsafeRawPointer 
  32.     // i8* 存放pwt指針 
  33.     var pwt: UnsafeRawPointer 
  34.  
  35. // 將circle強轉為protocolData結構體 
  36. withUnsafePointer(to: &rectangle) { ptr in 
  37.     ptr.withMemoryRebound(to: protocolData.self, capacity: 1) { pointer in 
  38.         print(pointer.pointee) 
  39.     } 
  40.  
  41. <!--打印結果--> 
  42. protocolData(value1: 0x4024000000000000, value2: 0x4034000000000000, value3: 0x0000000000000000, type: 0x0000000100004098, pwt: 0x0000000100004028) 

此時我們可以看到,此時并沒有存儲一個HeapObject結構的指針,而是直接存儲Double類型的值,metadata和pwt沒有變。

在看下IR代碼:

  1. define i32 @main(i32 %0, i8** %1) #0 { 
  2. entry: 
  3.   %2 = bitcast i8** %1 to i8* 
  4.   %3 = call swiftcc { doubledouble } @"main.Rectangle.init(Swift.Double, Swift.Double) -> main.Rectangle"(double 1.000000e+01, double 2.000000e+01) 
  5.   ; 10 
  6.   %4 = extractvalue { doubledouble } %3, 0 
  7.   ; 20 
  8.   %5 = extractvalue { doubledouble } %3, 1 
  9.   ;metadata 
  10.   store %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, i32 }>, <{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32, i32 }>* @"full type metadata for main.Rectangle", i32 0, i32 1) to %swift.type*), %swift.type** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.rectangle : main.Shape", i32 0, i32 1), align 8 
  11.   ;pwt 
  12.   store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"protocol witness table for main.Rectangle : main.Shape in main", i32 0, i32 0), i8*** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.rectangle : main.Shape", i32 0, i32 2), align 8 
  13.   ;存%4 也就是10 
  14.   store double %4, double* getelementptr inbounds (%T4main9RectangleV, %T4main9RectangleV* bitcast (%T4main5ShapeP* @"main.rectangle : main.Shape" to %T4main9RectangleV*), i32 0, i32 0, i32 0), align 8 
  15.   ; 存%5 也就是20 
  16.   store double %5, double* getelementptr inbounds (%T4main9RectangleV, %T4main9RectangleV* bitcast (%T4main5ShapeP* @"main.rectangle : main.Shape" to %T4main9RectangleV*), i32 0, i32 1, i32 0), align 8 

通過IR代碼我們可以看到:

  • 對于metadata和pwt的存儲依舊
  • 然后存儲了兩個Double值,并沒有存儲HeapObject類型的指針

那么如果有3個屬性呢?

  1. struct Rectangle: Shape{ 
  2.     var width, width1, height: Double 
  3.     init(_ width: Double, _ width1: Double, _ height: Double) { 
  4.         self.width = width 
  5.         self.width1 = width1 
  6.         self.height = height 
  7.     } 
  8.  
  9.     var area: Double
  10.         get{ 
  11.             return width * height 
  12.         } 
  13.     } 
  14.  
  15. <!--內存綁定后的打印結果--> 
  16. protocolData(value1: 0x4024000000000000, value2: 0x4034000000000000, value3: 0x403e000000000000, type: 0x0000000100004098, pwt: 0x0000000100004028) 

這個三個Value的值分別是10,20,30

那如果是4個呢?

  1. struct Rectangle: Shape{ 
  2.     var width, width1, height, height1: Double 
  3.     init(_ width: Double, _ width1: Double, _ height: Double, _ height1: Double) { 
  4.         self.width = width 
  5.         self.width1 = width1 
  6.         self.height = height 
  7.         self.height1 = height1 
  8.     } 
  9.  
  10.     var area: Double
  11.         get{ 
  12.             return width * height 
  13.         } 
  14.     } 
  15.  
  16. var rectangle: Shape = Rectangle(10.0, 20.0, 30.0, 40.0) 
  17.  
  18. <!--內存綁定后的打印結果--> 
  19. protocolData(value1: 0x0000000100715870, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x00000001000040c0, pwt: 0x0000000100004050) 

此時并沒有直接看到Double值了,查看value1的內存:

此時我們可以看到,這個內存中存儲了 10,20,30,40 這四個值。

所以如果我們需要存儲的數據超過了 24 x i8*,也就是 24 字節時,就會開辟內存空間進行存儲。這里只存儲指向新開辟內存空間的指針。

這里的順序是,如果不夠存儲就直接開辟內存空間,存儲值,記錄指針。而不是先存儲不夠了在開辟內存空間。

我們都知道,結構體是值類型,如果超過這 24 字節的存儲空間就會開辟內存用來存儲結構體中的值,如果此時發生拷貝會是神馬結構呢?下面我們就來驗證一下:

結構體拷貝:

  1. protocol Shape { 
  2.     var area: Double {get} 
  3. struct Rectangle: Shape{ 
  4.     var width, width1, height, height1: Double 
  5.     init(_ width: Double, _ width1: Double, _ height: Double, _ height1: Double) { 
  6.         self.width = width 
  7.         self.width1 = width1 
  8.         self.height = height 
  9.         self.height1 = height1 
  10.     } 
  11.  
  12.     var area: Double
  13.         get{ 
  14.             return width * height 
  15.         } 
  16.     } 
  17.  
  18. var rectangle: Shape = Rectangle(10.0, 20.0, 30.0, 40.0) 
  19. var rectangle1 = rectangle 
  20.  
  21. struct HeapObject { 
  22.     var type: UnsafeRawPointer 
  23.     var refCount1: UInt32 
  24.     var refCount2: UInt32 
  25.  
  26.  
  27. struct protocolData { 
  28.     //24 * i8 :因為是8字節讀取,所以寫成3個指針 
  29.     var value1: UnsafeRawPointer 
  30.     var value2: UnsafeRawPointer 
  31.     var value3: UnsafeRawPointer 
  32.     //type 存放metadata,目的是為了找到Value Witness Table 值目錄表 
  33.     var type: UnsafeRawPointer 
  34.     // i8* 存放pwt指針 
  35.     var pwt: UnsafeRawPointer 
  36.  
  37. // 內存重綁定 
  38. withUnsafePointer(to: &rectangle) { ptr in 
  39.     ptr.withMemoryRebound(to: protocolData.self, capacity: 1) { pointer in 
  40.         print(pointer.pointee) 
  41.     } 
  42.  
  43. withUnsafePointer(to: &rectangle1) { ptr in 
  44.     ptr.withMemoryRebound(to: protocolData.self, capacity: 1) { pointer in 
  45.         print(pointer.pointee) 
  46.     } 
  47.  
  48. <!--打印結果--> 
  49. protocolData(value1: 0x000000010683bac0, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x00000001000040c0, pwt: 0x0000000100004050) 
  50. protocolData(value1: 0x000000010683bac0, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x00000001000040c0, pwt: 0x0000000100004050) 

此時我們看到打印結果是一樣的。

那么修改呢?

添加如下代碼:

  1. protocol Shape { 
  2.     // 為了方便修改,在這聲明一下 
  3.     var width: Double {get set
  4.     var area: Double {get} 
  5.  
  6. rectangle1.width = 50 

通過 lldb 重新打印,我們可以看到在修改值后,內存地址已經修改了,此時就是寫時復制。當復制時并沒有值的修改,所以兩個變量指向同一個堆區內存。當修改變量的時候,會原本的堆區內存的值拷貝到一個新的內存區域,并進行值的修改。

如果我們將 struct 修改成 class,這里并不會觸發寫時復制,因為在 Swift 中類是引用類型,修改類的值就是修改其引用地址中的值。這里就不驗證了,感興趣的可以自己去試試。

如果我們將 Double 換成 String 原理也是一致的,這里也就不一一驗證了。

4.5.3 小結

至此我們也就清楚了,為什么協議中通過 witness_method 調用,最終能找到 V-Table 中的方法,原因就是存儲了 metadata 和 pwt。這也是我們都聲明為協議類型,最終能打印出不同形狀的面積根本原因。

5. 總結

至此我們對Swift中協議的分析就結束了,現總結如下:

1.Swift中類、結構體、枚舉都可以遵守協議

2.遵守多個協議使用逗號(,)分隔

3.有父類的,父類寫在前面,協議在后面用逗號(,)分隔

4.協議中可以添加屬性

  • 屬性可以是實例屬性和類型屬性
  • 屬性需要使用var修飾,不能屬于let
  • 類型屬性只能使用static修飾,不能使用class
  • 我們需要聲明屬性必須是可讀的或者可讀可寫的

5.協議中可以添加方法

  • 可以是實例方法或類方法
  • 像普通方法一樣放在協議定義中,但不需要大括號和方法體
  • 協議中不支持為協議中的方法提供默認參數
  • 協議中的類方法也只能使用static關鍵字作為前綴,不能使用class
  • 可以使用mutating提供異變方法,以使用該方法時修改實體的屬性等。
  • 可以定義構造方法,但是使用的時候需要使用required關鍵字

6.如果定義由類專屬協議,則需要繼承自AnyObject

7.協議可以作為類型

  • 作為函數、方法或構造器中的參數類型或返回值類型
  • 作為常量、變量或屬性的類型
  • 作為數組、字典或其他容器中的元素類型

8.協議的底層存儲結構是:24字節的ValueBuffer+ metadata(8字節,也就是vwt) + pwt(8字節)

  • 前24字節,官方說法是ValueBuffer,主要用于存儲遵循了協議的實體的屬性值
  • 如果超過ValueBuffer最大容量就會開辟內存進行存儲,此24字節拿出8字節存儲指向該內存區域的指針
  • 目前對于類,發現其存儲的都是指針
  • 存儲metadata是為了查找遵守協議的實體中實現協議的方法
  • pwt就是protocol witness table協議目擊表,存儲協議中的方法

 

責任編輯:姜華 來源: Swift 社區
相關推薦

2021-04-14 14:16:58

HttpHttp協議網絡協議

2021-05-10 07:38:09

Swift 泛型Tips

2021-04-20 06:12:09

Swift 反射 Mirror反射機制

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2022-04-29 14:38:49

class文件結構分析

2021-07-21 09:48:20

etcd-wal模塊解析數據庫

2021-03-12 09:21:31

MySQL數據庫邏輯架構

2021-04-01 10:51:55

MySQL鎖機制數據庫

2024-06-13 08:34:48

2023-03-29 07:45:58

VS編輯區編程工具

2022-02-17 08:53:38

ElasticSea集群部署

2021-01-28 08:55:48

Elasticsear數據庫數據存儲

2021-06-21 14:36:46

Vite 前端工程化工具

2022-03-22 09:09:17

HookReact前端

2021-04-08 11:00:56

CountDownLaJava進階開發

2021-07-08 07:30:13

Webpack 前端Tree shakin

2023-03-13 09:31:04

2021-10-28 08:51:53

GPIO軟件框架 Linux

2022-02-25 15:50:05

OpenHarmonToggle組件鴻蒙

2021-05-08 08:36:40

ObjectString前端
點贊
收藏

51CTO技術棧公眾號

在线观看国产黄| 18精品爽国产三级网站| 日本不良网站在线观看| 久久久久久久久久看片| 成人信息集中地欧美| www.99re7.com| av中文字幕一区二区| 欧美一区二区视频在线观看| 精品视频在线观看一区| 国产福利电影在线| 大胆亚洲人体视频| 国产精品爽爽爽爽爽爽在线观看| 国产在线视频二区| 91久久夜色精品国产按摩| 精品91自产拍在线观看一区| 久热精品在线观看视频| yellow字幕网在线| 亚洲色图制服诱惑| 亚洲成人第一| 日本高清中文字幕二区在线| 国产老女人精品毛片久久| 秋霞av国产精品一区| 久久激情免费视频| 久久电影院7| 亚洲片av在线| xxxxxx黄色| 精品视频在线一区| 欧美日韩一区国产| 超碰网在线观看| 国语对白在线刺激| 中文字幕一区二区三区乱码在线| 蜜桃视频日韩| 三级网站在线看| 国产一区视频网站| 成人网中文字幕| 日韩黄色一级视频| 国产精品嫩草99av在线| 久久久久国产精品免费| 亚洲天堂一级片| 日韩精品一区二区久久| 亚洲欧美日韩另类| 欧美无人区码suv| 98视频精品全部国产| 欧美一区二区三区在| 亚洲久久中文字幕| 久久三级毛片| 欧美日本高清视频在线观看| 无码少妇一区二区三区芒果| 欧美xoxoxo| 色欧美片视频在线观看| 国产在线青青草| 乱馆动漫1~6集在线观看| 午夜av一区二区| 97视频久久久| 涩涩涩在线视频| 欧美日韩免费一区| 99色精品视频| 亚洲国产尤物| 欧美日韩精品一区二区天天拍小说| 中文字幕在线观看第三页| 91九色综合| 欧美日韩国产一级片| 思思久久精品视频| 国产精品成人**免费视频| 日韩午夜电影av| 在线观看一区二区三区视频| 57pao国产一区二区| 精品国产成人系列| 亚洲av无码一区二区三区网址| 给我免费播放日韩视频| 日韩精品高清视频| 大黑人交xxx极品hd| 激情婷婷综合| www.xxxx欧美| 久久久综合久久久| 国产精品呻吟| 国产精品视频免费观看www| 亚洲一级在线播放| 国产suv精品一区二区6| 久久精品二区| 色欧美激情视频在线| 亚洲免费观看高清完整| 免费观看国产精品视频| 亚洲精品在线影院| 日韩一区二区视频| 野花社区视频在线观看| 欧洲激情视频| 欧美高清视频在线| 神马久久久久久久| 国产伦精品一区二区三区视频青涩| 成人在线观看91| 黑人与亚洲人色ⅹvideos| 综合久久一区二区三区| 99色这里只有精品| 亚洲日本在线观看视频| 日韩免费看网站| 中文字幕在线看高清电影| 国产精品成人a在线观看| 久久久久免费视频| 中文字幕一区二区三区四区免费看 | 波多野结衣视频一区| 日本亚洲欧洲精品| 免费毛片在线看片免费丝瓜视频| 欧美最猛黑人xxxxx猛交| 一起草最新网址| 成人羞羞动漫| 2019中文在线观看| 精品黑人一区二区三区在线观看| 久久久久久久综合色一本| 欧美xxxx吸乳| 台湾成人免费视频| 日韩国产一区三区| 精国产品一区二区三区a片| 久久免费高清| 国产三级精品在线不卡| free性欧美hd另类精品| 日本精品视频一区二区三区| 亚洲熟妇一区二区| 97精品视频| 国产精品69久久| 五月婷婷开心中文字幕| 亚洲精品大片www| 欧美三级午夜理伦三级富婆| 日韩在线你懂的| 欧美极品美女电影一区| 国产免费福利视频| 中文字幕第一区综合| 北条麻妃av高潮尖叫在线观看| 国产亚洲精品美女久久| 欧美另类高清videos| 在线免费观看av片| 国产精品欧美经典| 簧片在线免费看| 九九亚洲视频| 欧美专区在线观看| 午夜福利一区二区三区| 亚洲福利一二三区| 男男一级淫片免费播放| 午夜电影亚洲| 成人免费视频视频在| av毛片在线播放| 日韩一区二区在线免费观看| 国产又粗又长又硬| 久久99久久久欧美国产| 亚洲一区二区三区精品动漫| 欧美黄色成人| 精品国产一区二区三区久久狼5月| 中文字幕av第一页| 国产精品视频你懂的| 免费涩涩18网站入口| 日韩夫妻性生活xx| 91精品在线播放| 精品视频在线一区二区| 欧美一区二区三区四区高清| 欧美激情精品久久久久久免费| 久久成人综合网| 国产日本欧美在线| 99久久香蕉| 91干在线观看| 国产成人天天5g影院在线观看| 91黄色免费观看| 日本视频在线免费| 国产成人aaaa| 欧美性久久久久| 精品美女在线视频| 成人亲热视频网站| 91九色在线播放| 亚洲欧洲在线看| 国产精品久久777777换脸| 亚洲人成伊人成综合网小说| 日本中文字幕有码| 亚洲一区网站| 中文字幕中文字幕在线中一区高清| 亚洲精品三区| 久久久欧美一区二区| 国产综合在线观看| 日韩一二三区视频| 天天干天天干天天| 中文字幕亚洲一区二区va在线| 亚洲AV无码久久精品国产一区| 精品91视频| 日韩欧美一区二区视频在线播放| 91成人精品观看| 91国内免费在线视频| 91免费在线| 欧美tickling挠脚心丨vk| yjizz国产| 亚洲色大成网站www久久九九| 在线xxxxx| 青青草成人在线观看| 99久热在线精品视频| 杨幂一区二区三区免费看视频| 成人在线播放av| 在线视频超级| 欧美成人一区二区三区电影| 欧美成人免费| 日韩欧美电影一区| 中文字幕在线播出| 欧美日韩性视频| 丁香花五月激情| 国产午夜精品久久久久久久 | 无罩大乳的熟妇正在播放| 精品免费在线| 国产有色视频色综合| 自拍偷拍亚洲图片| 国产福利精品av综合导导航| 久久99亚洲网美利坚合众国| 神马久久桃色视频| 日产精品久久久久久久性色| 日韩一级在线观看| 亚洲天堂中文在线| 福利视频第一区| 免费一级黄色大片| 中文字幕在线观看不卡| 精品无人区无码乱码毛片国产| 懂色av噜噜一区二区三区av| 在线能看的av网站| 日本在线不卡一区| 欧美日韩亚洲一| 欧美精品一卡| 国产高清免费在线| 欧美日韩在线二区| 日本在线观看一区二区三区| 精品三级av| 国产精品久久亚洲| 伊人久久噜噜噜躁狠狠躁| 国产欧美日韩综合精品| 欧美三区四区| 国产999精品| 亚洲精品国产精品国产| 国语自产偷拍精品视频偷 | 教室别恋欧美无删减版| 国产精选一区二区| 激情av综合| 国产精品区一区| 亚洲一区二区三区中文字幕在线观看| 成人xxxxx| 成人在线分类| 3d蒂法精品啪啪一区二区免费| 日韩电影精品| 成人免费视频a| 精品一区二区三区四区五区| 91精品视频一区| 疯狂欧洲av久久成人av电影| 91香蕉国产在线观看| 成人亚洲精品| 成人免费91在线看| 久9re热视频这里只有精品| 精品国产乱码久久久久久郑州公司| 99国产精品免费网站| 成人国内精品久久久久一区| 国产精品1区在线| 成人综合色站| 久久久久观看| 欧美在线一区二区三区四区| 国产一区二区区别| 亚洲精品成人自拍| 亚洲激情五月| 欧美精品在欧美一区二区| 国产主播一区| 国模无码视频一区二区三区| 久久精品观看| 性欧美1819| 国产高清不卡一区| 懂色av粉嫩av蜜乳av| 国产日韩三级在线| 暗呦丨小u女国产精品| 一区二区三区在线播| 日本免费观看视| 在线精品国精品国产尤物884a| 中文字幕乱码在线观看| 欧美一区二区三区在| 亚洲av成人无码久久精品老人| 亚洲欧美成人精品| 日本视频在线观看| 午夜精品久久久久久久久久久久| 中文字幕在线看片| 91精品久久久久| 麻豆成人入口| 一区二区三区四区视频在线| 欧美日韩免费| 国产免费视频传媒| 国产精一品亚洲二区在线视频| 给我看免费高清在线观看| 国产精品天美传媒| 国产精品6666| 欧美日韩国产a| 天堂在线观看视频| 久久激情视频久久| 久草在线资源福利站| 国产精品揄拍500视频| 精品亚洲自拍| 国产又粗又大又爽的视频| 国产精品女主播一区二区三区| 99re6在线观看| 久久综合成人精品亚洲另类欧美 | 亚洲久久成人| 久热精品在线播放| av不卡在线观看| 疯狂试爱三2浴室激情视频| 欧美日韩激情小视频| 91九色蝌蚪91por成人| 日韩经典第一页| 免费男女羞羞的视频网站在线观看| 国产精品成人久久久久| 国产美女撒尿一区二区| 男插女免费视频| 丝袜美腿亚洲色图| 又黄又爽的网站| 亚洲欧美一区二区三区国产精品| 天干夜夜爽爽日日日日| 精品国产电影一区二区| a级网站在线播放| 国产精品视频一| 在线日韩一区| 久在线观看视频| 成人午夜私人影院| 日本精品人妻无码77777| 欧美在线免费观看亚洲| 日本中文字幕电影在线观看 | 国产黄色精品| 日本视频精品一区| 99综合在线| 性久久久久久久久久久| 亚洲精品国产精品乱码不99| 97精品久久人人爽人人爽| 亚洲午夜色婷婷在线| 亚洲精品一区| 欧美日韩成人一区二区三区| 亚洲高清毛片| 久久久高清视频| 亚洲图片有声小说| www.久久成人| 欧美精品做受xxx性少妇| 99视频这里有精品| 在线综合视频网站| 韩国成人精品a∨在线观看| 激情高潮到大叫狂喷水| 欧美美女喷水视频| 蜜芽在线免费观看| 成人春色激情网| 中文字幕免费一区二区三区| 小早川怜子一区二区三区| 国产精品福利在线播放| 国产精品久久久久久久久久久久久久久久| 亚洲小视频在线观看| 亚洲伦理影院| 亚洲欧美精品在线观看| 美女视频一区在线观看| 老熟妇高潮一区二区三区| 欧美妇女性影城| 污污视频在线| 国产精品国产三级国产专区53| 亚洲网站啪啪| 国产精品无码一区二区三区免费| 欧美日韩国产丝袜美女| 毛片在线免费| 国产日本欧美在线观看| 亚洲欧美网站在线观看| 麻豆av免费看| 欧美日韩在线影院| 福利片在线看| 亚洲一区久久久| 尤物在线精品| 欧美 日韩 国产 成人 在线观看| 欧美在线观看一区二区| 欧美69xxx| 国产久一道中文一区| 天堂成人免费av电影一区| 99久久99久久精品免费看小说.| 制服丝袜亚洲色图| 4438x成人网全国最大| 久久国产精品免费一区| 日本成人在线视频网站| 澳门黄色一级片| 日韩精品免费电影| 精品无人乱码一区二区三区| 永久免费精品视频网站| 成人午夜激情影院| 国产天堂第一区| 欧美大片在线看| 欧美精选视频在线观看| 污视频在线观看免费网站| 亚洲国产你懂的| 99re在线视频| 国产综合色一区二区三区| 蜜臀av性久久久久蜜臀av麻豆| 激情五月婷婷在线| 亚洲视频axxx| 91蝌蚪精品视频| 国产视频手机在线播放| 亚洲国产中文字幕| 1769视频在线播放免费观看| 国产欧美在线一区二区| 精品一区二区在线播放| 中文字幕av影院| 色综合久久悠悠| 日韩欧美精品一区| 成人免费无码大片a毛片| 91精品久久久久久久91蜜桃 |