Swift底層進(jìn)階--013:協(xié)議

Protocol:所謂協(xié)議奠蹬,就是一組屬性和/或方法的定義专筷,而如果某個具體類型想要遵守一個協(xié)議强霎,那它需要實現(xiàn)這個協(xié)議所定義的所有這些內(nèi)容宣吱。協(xié)議實際上做的事情不過是“關(guān)于實現(xiàn)的約定”

  • Swift中協(xié)議被賦予更加強(qiáng)大、靈活的功能明未。相比Objective-C的協(xié)議槽华,Swift的協(xié)議不僅可以被用做代理,也可以用作對接口的抽象趟妥,對代碼的復(fù)用猫态。
  • 協(xié)議規(guī)定了用來實現(xiàn)某一特定功能所必需的方法和屬性。任意能夠滿足協(xié)議要求的類型被稱為遵循(conform)協(xié)議披摄。
  • 類亲雪,結(jié)構(gòu)體或枚舉類型都可以遵循協(xié)議,并提供具體實現(xiàn)來完成協(xié)議定義的方法和功能行疏。
基本?法
語法格式
protocol MyProtocol {
   // 屬性
   // 方法
}

class匆光,structenum都可以遵循協(xié)議酿联,如果要遵守多個協(xié)議终息,使?逗號分隔

struct LGTeacher: Protocol1, Protocol2 {
   // 屬性
   // 方法
}

如果class中有superClass夺巩,?般放在遵循的協(xié)議之前

class LGTeacher: NSObject, MyProtocol{
   // 屬性
   // 方法
}
協(xié)議中屬性定義的要求
  • 屬性不能設(shè)置默認(rèn)值
  • 屬性必須明確是可讀或可讀可寫
  • 屬性必須使用var修飾
協(xié)議中方法定義的要求
  • 方法不能有方法體
  • 方法參數(shù)不能設(shè)置默認(rèn)值
遵循協(xié)議并實現(xiàn)協(xié)議中的屬性、方法
protocol MyProtocol {
    var age: Int { get }
    var name: String { get set }
    
    func doSomething(age: Int) -> Int
    static func teach()
}

class LGTeacher: MyProtocol {
    let age: Int = 18
    var name: String = "Zang"
    
    func doSomething(age: Int = 18) -> Int {
        print("LGTeacher doSomething")
        return age
    }
    
    static func teach() {
        print("teach")
    }
}

LGTeacher遵循MyProtocol協(xié)議

  • 實現(xiàn)協(xié)議的屬性周崭,可以設(shè)置默認(rèn)值柳譬。只讀屬性可以選擇let修飾
  • 實現(xiàn)協(xié)議的方法,參數(shù)可以設(shè)置默認(rèn)值
  • 協(xié)議中使用static修飾的類型方法续镇,可以被類或結(jié)構(gòu)體遵循螟凭。被類遵循历极,可以選擇將static替換為class修飾

func使用static或者class修飾的區(qū)別與聯(lián)系

  • static或者class關(guān)鍵字,都可用于指定類方法
  • class關(guān)鍵字指定的類方法可以被子類重寫
  • static關(guān)鍵字指定的類方法不能被子類重寫
required關(guān)鍵字

協(xié)議也可以定義初始化?法,當(dāng)類實現(xiàn)初始化器的時候何乎,必須使?required關(guān)鍵字

protocol MyProtocol {
    init(age: Int)
}

class LGTeacher: MyProtocol{
    var age: Int
    
    required init(age: Int) {
        self.age = age
    }
}

如果當(dāng)前類被final修飾蚜印,可以忽略required關(guān)鍵字担猛,因為被final修飾的類不能被繼承

protocol MyProtocol: AnyObject {
    init(age: Int)
}

final class LGTeacher: MyProtocol{
    var age: Int

    init(age: Int) {
        self.age = age
    }
}

使用final修飾的類被繼承松蒜,編譯報錯

編譯報錯

要求協(xié)議只能被類遵循

如果要求協(xié)議只能被類遵循,可以添加AnyObject

protocol MyProtocol: AnyObject {
    init(age: Int)
}

class LGTeacher: MyProtocol{
    var age: Int

    required init(age: Int) {
        self.age = age
    }
}

當(dāng)structenum遵循MyProtocol協(xié)議读串,編譯報錯

編譯報錯

將協(xié)議作為類型
  • 作為函數(shù)聊记、?法或初始化程序中的參數(shù)類型或返回類型
  • 作為常量、變量或?qū)傩缘念愋?/li>
  • 作為數(shù)組恢暖、字典或其他容器中項?的類型
案例1:

MyProtocol協(xié)議定義teach方法排监,在MyProtocol擴(kuò)展和LGTeacher中都實現(xiàn)teach方法,查看案例1的打印結(jié)果:

protocol MyProtocol {
    func teach()
}

extension MyProtocol {
    func teach() {
        print("MyProtocol")
    }
}

class LGTeacher: MyProtocol {
    func teach() {
        print("MyClass")
    }
}

let t1: MyProtocol = LGTeacher()
t1.teach()

let t2: LGTeacher = LGTeacher()
t2.teach()

//輸出以下內(nèi)容:
//MyClass
//MyClass
  • t1聲明MyProtocol類型杰捂,protocol中定義了teach?法舆床,使用witness_method調(diào)用,通過PWT協(xié)議?擊表獲取對應(yīng)的函數(shù)地址琼娘,打印結(jié)果MyClass
  • t2聲明LGTeacher類型峭弟,使用class_method調(diào)用,通過V-table函數(shù)表查找函數(shù)脱拼,打印結(jié)果MyClass

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

main

PWT協(xié)議?擊表,LGTeacher遵循MyProtocol協(xié)議坷备,并記錄實現(xiàn)的方法

PWT

遵循協(xié)議后被實現(xiàn)的協(xié)議方法teach熄浓,內(nèi)部使用class_method指令通過類的函數(shù)表查找函數(shù)

bb0

案例2:

如果MyProtocol是空協(xié)議,在MyProtocol擴(kuò)展和LGTeacher中實現(xiàn)teach方法省撑,查看案例2的打印結(jié)果:

protocol MyProtocol {}

extension MyProtocol {
    func teach() {
        print("MyProtocol")
    }
}

class LGTeacher: MyProtocol {
    func teach() {
        print("MyClass")
    }
}

let t1: MyProtocol = LGTeacher()
t1.teach()

let t2: LGTeacher = LGTeacher()
t2.teach()

//輸出以下內(nèi)容:
//MyProtocol
//MyClass
  • t1聲明MyProtocol類型赌蔑,protocol中未定義任何?法,直接使用extension靜態(tài)調(diào)用竟秫。靜態(tài)調(diào)用在編譯鏈接之后方法地址已經(jīng)確定娃惯,對于遵循協(xié)議的類來說?法重寫,打印結(jié)果MyProtocol
  • t2聲明LGTeacher類型肥败,使用class_method調(diào)用趾浅,通過V-table函數(shù)表查找函數(shù)愕提,打印結(jié)果MyClass

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

main

PWT協(xié)議?擊表,雖然LGTeacher遵循MyProtocol協(xié)議皿哨,但沒有記錄任何方法浅侨,同時也找不到上面定義的被實現(xiàn)的協(xié)議方法

PWT

案例3:

MyProtocol協(xié)議定義teach方法,僅MyProtocol擴(kuò)展實現(xiàn)teach方法证膨,查看案例3的打印結(jié)果:

protocol MyProtocol {
    func teach()
}

extension MyProtocol {
    func teach() {
        print("MyProtocol")
    }
}

class LGTeacher: MyProtocol {}

let t1: MyProtocol = LGTeacher()
t1.teach()

let t2: LGTeacher = LGTeacher()
t2.teach()

//輸出以下內(nèi)容:
//MyProtocol
//MyProtocol

t1聲明MyProtocol類型如输,protocol中定義了teach?法,使用witness_method調(diào)用央勒,通過PWT協(xié)議?擊表獲取對應(yīng)的函數(shù)地址不见,打印結(jié)果MyProtocol
t2聲明LGTeacher類型,由于類中沒有實現(xiàn)teach方法崔步,直接使用extension靜態(tài)調(diào)?稳吮,打印結(jié)果MyProtocol

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

main

PWT協(xié)議?擊表,LGTeacher遵循MyProtocol協(xié)議刷晋,并記錄實現(xiàn)的方法

PWT

遵循協(xié)議后被實現(xiàn)的協(xié)議方法teach盖高,內(nèi)部使用extension靜態(tài)調(diào)用

bb0

PWT
案例1

定義Circle類遵循Shape協(xié)議,聲明Shape類型和Circle類型實例變量眼虱,打印各自的大小和步長

protocol Shape{
    var area: Double{ get }
}

class Circle: Shape {
    var radious: Double
    init(_ radious: Double) {
        self.radious = radious
    }

    var area: Double{
        get{
            return radious * radious * 3.14
        }
    }
}

var shape: Shape = Circle.init(10.0)
print("shape of size:\(MemoryLayout.size(ofValue: shape))")
print("shape of stride:\(MemoryLayout.stride(ofValue: shape))")

var circle: Circle = Circle.init(10.0)
print("circle of size:\(MemoryLayout.size(ofValue: circle))")
print("circle of stride:\(MemoryLayout.stride(ofValue: circle))")

//輸出以下內(nèi)容:
//shape of size:40
//shape of stride:40
//circle of size:8
//circle of stride:8

shape聲明為Shape類型喻奥,大小和步長占40字節(jié)circle聲明為Circle類型捏悬,大小和步長只占8字節(jié)

使用lldb調(diào)式撞蚕,能看到的內(nèi)容有限,無法得出結(jié)論

lldb

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

main

通過官方文檔过牙,查看init_existential_addr的作用

init_existential_addr

Existential Container是編譯器?成的?種特殊數(shù)據(jù)類型甥厦,?于管理遵守了相同協(xié)議的協(xié)議類型。因為這些數(shù)據(jù)類型的內(nèi)存空間尺?不同寇钉,使?Extential Container進(jìn)?管理可以實現(xiàn)存儲?致性

如果想進(jìn)一步分析刀疙,明確init_existential_addr存儲的到底是什么,需要將SIL代碼再降?級扫倡,通過IR代碼觀察

將上述代碼生成IR文件:swiftc -emit-ir main.swift | xcrun swift-demangle

IR

%T4main5ShapeP是一個結(jié)構(gòu)體谦秧,有一個24 x i8的數(shù)組,占24字節(jié)撵溃。有一個%swift.type*的指針和一個i8**的二級指針

%T4main5ShapeP = type { [24 x i8], %swift.type*, i8** }

%4存儲的是metadata

%4 = extractvalue %swift.metadata_response %3, 0

%5存儲的是HeapObject疚鲤,它調(diào)用了__allocating_init,傳入了一個double類型和一個metadata

%5 = call swiftcc %T4main6CircleC* @"main.Circle.__allocating_init(Swift.Double) -> main.Circle"(double 1.000000e+01, %swift.type* swiftself %4)

圖中第一句代碼缘挑,獲取結(jié)構(gòu)體本身集歇,拿到第一個元素,然后將%4也就是metadata语淘,存儲到結(jié)構(gòu)體的第一個元素中

store %swift.type* %4, %swift.type** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.shape : main.Shape", i32 0, i32 1), align 8

圖中第二句代碼诲宇,獲取結(jié)構(gòu)體本身际歼,拿到第二個元素,然后將PWT協(xié)議目擊表焕窝,存儲到結(jié)構(gòu)體的第二個元素中蹬挺。代碼中@"protocol witness table for main.Circle : main.Shape in main"就是PWT

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.shape : main.Shape", i32 0, i32 2), align 8

圖中第三句代碼,實例對象main.shape是結(jié)構(gòu)體首地址24 x i8數(shù)組類型它掂,
使用bitcast將其強(qiáng)轉(zhuǎn)為%T4main6CircleC二級指針類型巴帮,再將%5也就是HeapObject存儲到強(qiáng)轉(zhuǎn)后的指針中

store %T4main6CircleC* %5, %T4main6CircleC** bitcast (%T4main5ShapeP* @"main.shape : main.Shape" to %T4main6CircleC**), align 8

最終可以分析出%T4main5ShapeP結(jié)構(gòu)體的元素構(gòu)成

type { HeapObject, metadata, PWT } 

使用Swift代碼還原上述結(jié)論

定義HeapObject結(jié)構(gòu)體

struct HeapObject{
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}

定義ProtocolData結(jié)構(gòu)體,value1虐秋、value2榕茧、value3三個屬性,各自占8字節(jié)客给,連續(xù)內(nèi)存空間存儲用押,對應(yīng)IR代碼中的24 x i8數(shù)組。type對應(yīng)metadata靶剑,pwt對應(yīng)PWT協(xié)議目擊表

struct ProtocolData {
    var value1: UnsafeRawPointer
    var value2: UnsafeRawPointer
    var value3: UnsafeRawPointer
    var type: UnsafeRawPointer
    var pwt: UnsafeRawPointer
}

將實例對象shape轉(zhuǎn)換為ProtocolData結(jié)構(gòu)

withUnsafePointer(to: &shape){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        print(pointer.pointee)
    }
}

//輸出以下結(jié)果:
//ProtocolData(value1: 0x0000000100705910, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x000000010000c3b0, pwt: 0x0000000100008070)
  • value1存儲HeapObject地址蜻拨,value2value3未被使用
  • type存儲的metadata桩引,實際上是VWT(Value Witness Table)
  • pwt存儲的PWT協(xié)議目擊表

進(jìn)入終端缎讼,使用nm命令查看pwt存儲的0000000100008070地址:

nm /Users/zang/Library/Developer/Xcode/DerivedData/LGSwiftTest-ezhaqfxldtlovoammsyvcyhxjxoj/Build/Products/Debug/LGSwiftTest | grep 0000000100008070

輸出結(jié)果:

0000000100008070 S _$s11LGSwiftTest6CircleCAA5ShapeAAWP

使用xcrun還原符號:

xcrun swift-demangle s11LGSwiftTest6CircleCAA5ShapeAAWP

輸出結(jié)果:

$s11LGSwiftTest6CircleCAA5ShapeAAWP ---> protocol witness table for LGSwiftTest.Circle : LGSwiftTest.Shape in LGSwiftTest

這也解釋了為什么上面看到的Shape類型實例對象會占40字節(jié);其中value1坑匠、value2血崭、value3三個屬性共占24字節(jié)typepwt共占16字節(jié)

案例2

如果定義的是結(jié)構(gòu)體厘灼,遵循Shape協(xié)議夹纫,存儲的結(jié)構(gòu)會有哪些變化?

protocol Shape{
    var area: Double{ get }
}

struct Rectangle: Shape {
    var width, height: Double

    init(_ width: Double, _ height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double{
        get{
            return width * height
        }
    }
}

var shape: Shape = Rectangle.init(10.0, 20.0)

將上述代碼生成IR文件:swiftc -emit-ir main.swift | xcrun swift-demangle

IR

圖中%4设凹、%5發(fā)生了變化舰讹,直接將結(jié)構(gòu)體的兩個double屬性取出來,存儲到%T4main5ShapeP結(jié)構(gòu)體首地址的24 x i8數(shù)組中

將結(jié)構(gòu)體類型的實例對象shape轉(zhuǎn)換為ProtocolData結(jié)構(gòu)

withUnsafePointer(to: &shape){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        print(pointer.pointee)
    }
}

//輸出以下結(jié)果:
//ProtocolData(value1: 0x4024000000000000, value2: 0x4034000000000000, value3: 0x0000000000000000, type: 0x00000001000040e0, pwt: 0x0000000100004070)

value1存儲width屬性闪朱,value2存儲height屬性跺涤,value3未被使用
type存儲的metadata,實際上是VWTValue Witness Table
pwt存儲的PWT協(xié)議目擊表

案例3

如果結(jié)構(gòu)體大小超過24字節(jié)监透,存儲的結(jié)構(gòu)會有哪些變化?

protocol Shape{
    var area: Double{ get }
}

struct Rectangle: Shape {
    var width, height: Double
    var width1: Double = 40.0
    var height1: Double = 50.0

    init(_ width: Double, _ height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double{
        get{
            return width * height
        }
    }
}

var shape: Shape = Rectangle.init(10.0, 20.0)

案例2的基礎(chǔ)上航唆,將結(jié)構(gòu)體增加width1胀蛮、height1兩個屬性,這時結(jié)構(gòu)體的大小已超過24字節(jié)

withUnsafePointer(to: &shape){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        print(pointer.pointee)
    }
}

//輸出以下結(jié)果:
//ProtocolData(value1: 0x0000000103304080, value2: 0x0000000000000000, value3: 0x0000000000000000, type: 0x0000000100004108, pwt: 0x0000000100004098)

只有value1有值糯钙,value2粪狼、value3未被使用
typepwt沒有變化

通過lldb退腥,查看value1存儲的是什么

lldb

  • 結(jié)構(gòu)體首地址24字節(jié),存儲的是遵循協(xié)議的結(jié)構(gòu)體或類的值
  • 如果是值類型再榄,大小在24字節(jié)以內(nèi)狡刘,直接存儲值。如果超過24字節(jié)困鸥,系統(tǒng)將在堆區(qū)分配內(nèi)存空間嗅蔬,將值存儲到堆區(qū),然后將堆區(qū)的指針地址存儲到前面8字節(jié)疾就,此時value2澜术、value3未被使用
  • 如果是引用類型,直接存儲HeapObject
案例4

聲明兩個協(xié)議類型實例對象猬腰,將它們存儲到數(shù)組鸟废。循環(huán)打印shape.area屬性時,它們可以正確調(diào)用到各自的實現(xiàn)方法

protocol Shape{
    var area: Double{ get }
}

class Circle: Shape {
    var radious: Double

    init(_ radious: Double) {
        self.radious = radious
    }

    var area: Double{
        get{
            return radious * radious * 3.14
        }
    }
}

struct Rectangle: Shape {
    var width, height: Double

    init(_ width: Double, _ height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double{
        get{
            return width * height
        }
    }
}

var circle: Shape = Circle.init(10.0)
var rectangle: Shape = Rectangle.init(10.0, 20.0)
var shapes: [Shape] = [circle, rectangle]

for shape in shapes{
    print(shape.area)
}

//輸出以下內(nèi)容:
//314.0
//200.0

理解了前三個案例姑荷,對于案例4的打印結(jié)果應(yīng)該不會意外盒延;本質(zhì)上協(xié)議容器里存放了PWT協(xié)議目擊表和metadata,在協(xié)議目擊表內(nèi)依然使用class_method查表方式鼠冕,通過metadata 找到對應(yīng)的V-table添寺,最終調(diào)用正確的實現(xiàn)方法

案例5

聲明Shape類型circle,將circle賦值給circle1供鸠,這時打印circlecircle1value1地址畦贸,會是相同地址嗎?如果修改circle1.radious屬性楞捂,他們value1的地址又會發(fā)生怎樣的變化薄坏?

protocol Shape{
    var radious: Double { get set }
    var area: Double { get }
}

struct Circle: Shape {
    var radious: Double
    var radious1: Double = 3
    var radious2: Double = 6
    var radious3: Double = 9

    init(_ radious: Double) {
        self.radious = radious
    }

    var area: Double{
        get{
            return radious * radious * 3.14
        }
    }
}

struct ValueData {
    var type: HeapObject
    var val1: Double
    var val2: Double
    var val3: Double
    var val4: Double
}

var circle: Shape = Circle.init(10.0)
var circle1 = circle

withUnsafePointer(to: &circle){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle.value1 - 修改前地址:\(pointer.pointee.value1),值:\(ptr.pointee.val1)")
    }
}

withUnsafePointer(to: &circle1){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle1.value1 - 修改前地址:\(pointer.pointee.value1)寨闹,值:\(ptr.pointee.val1)")
    }
}

circle1.radious = 20.0
print("\n----------\n")

withUnsafePointer(to: &circle){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle.value1 - 修改后地址:\(pointer.pointee.value1)胶坠,值:\(ptr.pointee.val1)")
    }
}

withUnsafePointer(to: &circle1){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle1.value1 - 修改后地址:\(pointer.pointee.value1),值:\(ptr.pointee.val1)")
    }
}

//輸出一下結(jié)果:
//circle.value1 - 修改前地址:0x0000000100406ae0繁堡,值:10.0
//circle1.value1 - 修改前地址:0x0000000100406ae0沈善,值:10.0
//
//----------
//
//circle.value1 - 修改后地址:0x0000000100406ae0,值:10.0
//circle1.value1 - 修改后地址:0x000000010052ed30椭蹄,值:20.0

circle1.radious修改前闻牡,circlecircle1value1地址是相同的。當(dāng)circle1.radious發(fā)生改變后绳矩,circle1.value1的地址也發(fā)生了改變罩润,這種現(xiàn)象稱之為“寫時復(fù)制”(copy-on-write

上述代碼為什么會觸發(fā)的“寫時復(fù)制”?
  • Circle是結(jié)構(gòu)體類型翼馆,也就是值類型割以,所以circlecircle1并不共享狀態(tài)
  • 當(dāng)circle1.radious發(fā)生改變金度,相當(dāng)于修改副本,對circle沒有任何影響
  • protocol結(jié)構(gòu)體中24字節(jié)官方叫法是Value Buffer严沥,Value Buffer用來存儲當(dāng)前的值猜极,如果超過Value Buffer的最大存儲容量,系統(tǒng)會開辟堆空間存儲值消玄,Value Buffer存儲堆區(qū)指針地址
  • 修改堆空間里的值類型跟伏,在修改前會先檢測引用計數(shù),如果引用計數(shù)大于1莱找,此時系統(tǒng)會開辟新的堆空間酬姆,把要修改的內(nèi)容拷貝到新空間內(nèi),目的是提升性能
  • 如果是引用類型Class奥溺,則不會觸發(fā)寫時復(fù)制
案例6

案例5的結(jié)構(gòu)體修改為Class辞色,查看輸出結(jié)果的變化

class Circle: Shape {
    var radious: Double

    init(_ radious: Double) {
        self.radious = radious
    }

    var area: Double{
        get{
            return radious * radious * 3.14
        }
    }
}

var circle: Shape = Circle.init(10.0)
var circle1 = circle

withUnsafePointer(to: &circle){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle.value1 - 修改前地址:\(pointer.pointee.value1),值:\(ptr.pointee.val1)")
    }
}

withUnsafePointer(to: &circle1){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle1.value1 - 修改前地址:\(pointer.pointee.value1)浮定,值:\(ptr.pointee.val1)")
    }
}

circle1.radious = 20.0
print("\n----------\n")

withUnsafePointer(to: &circle){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle.value1 - 修改后地址:\(pointer.pointee.value1)相满,值:\(ptr.pointee.val1)")
    }
}

withUnsafePointer(to: &circle1){ ptr in
    ptr.withMemoryRebound(to: ProtocolData.self, capacity: 1){ pointer in
        let ptr = pointer.pointee.value1.assumingMemoryBound(to: ValueData.self)
        print("circle1.value1 - 修改后地址:\(pointer.pointee.value1),值:\(ptr.pointee.val1)")
    }
}

//輸出一下結(jié)果:
//circle.value1 - 修改前地址:0x0000000103204a20桦卒,值:10.0
//circle1.value1 - 修改前地址:0x0000000103204a20立美,值:10.0
//
//----------
//
//circle.value1 - 修改后地址:0x0000000103204a20,值:20.0
//circle1.value1 - 修改后地址:0x0000000103204a20方灾,值:20.0

Circle修改為Class類型建蹄,也就是引用類型,所以circlecircle1共享狀態(tài)裕偿。當(dāng)circle1.radious發(fā)生改變洞慎,circle也會隨之改變

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘿棘,隨后出現(xiàn)的幾起案子劲腿,更是在濱河造成了極大的恐慌,老刑警劉巖鸟妙,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焦人,死亡現(xiàn)場離奇詭異,居然都是意外死亡重父,警方通過查閱死者的電腦和手機(jī)花椭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來房午,“玉大人个从,你說我怎么就攤上這事。” “怎么了嗦锐?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沪曙。 經(jīng)常有香客問我奕污,道長,這世上最難降的妖魔是什么液走? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任碳默,我火速辦了婚禮,結(jié)果婚禮上缘眶,老公的妹妹穿的比我還像新娘嘱根。我一直安慰自己,他們只是感情好巷懈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布该抒。 她就那樣靜靜地躺著,像睡著了一般顶燕。 火紅的嫁衣襯著肌膚如雪凑保。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天涌攻,我揣著相機(jī)與錄音欧引,去河邊找鬼。 笑死恳谎,一個胖子當(dāng)著我的面吹牛芝此,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播因痛,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婚苹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了婚肆?” 一聲冷哼從身側(cè)響起租副,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎较性,沒想到半個月后用僧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赞咙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年责循,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攀操。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡院仿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歹垫,我是刑警寧澤剥汤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站排惨,受9級特大地震影響吭敢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暮芭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一鹿驼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辕宏,春花似錦畜晰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至面哼,卻和暖如春野宜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背魔策。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工匈子, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闯袒。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓虎敦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親政敢。 傳聞我的和親對象是個殘疾皇子其徙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 協(xié)議的基本?法 協(xié)議的語法格式 我們熟悉的 class , struct , enum 都可以遵循協(xié)議,如果要遵守...
    Mjs閱讀 530評論 0 0
  • 協(xié)議為方法喷户、屬性唾那、以及其他特定的任務(wù)需求或功能定義藍(lán)圖。協(xié)議可被類褪尝、結(jié)構(gòu)體闹获、或枚舉類型采納以提供所需功能的具體實現(xiàn)...
    HotPotCat閱讀 1,528評論 2 5
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 3,837評論 0 6
  • C語?枚舉 先來回顧?下C語?的枚舉寫法: ?如表示?周 7天,?C語?的枚舉寫法應(yīng)該是這樣的: 第?個枚舉成員默...
    帥駝駝閱讀 607評論 0 3
  • 閉包是可以在你的代碼中被傳遞和引用的功能性獨(dú)立代碼塊河哑。閉包在實現(xiàn)上是一個結(jié)構(gòu)體避诽,它存儲了一個函數(shù)(通常是其入口地址...
    HotPotCat閱讀 1,024評論 1 4