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
匆光,struct
,enum
都可以遵循協(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)
struct
或enum
遵循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
地址蜻拨,value2
、value3
未被使用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é)
,type
和pwt
共占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
,實際上是VWT
(Value 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
未被使用
type
和pwt
沒有變化
通過
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
供鸠,這時打印circle
和circle1
的value1
地址畦贸,會是相同地址嗎?如果修改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
修改前闻牡,circle
和circle1
的value1
地址是相同的。當(dāng)circle1.radious
發(fā)生改變后绳矩,circle1.value1
的地址也發(fā)生了改變罩润,這種現(xiàn)象稱之為“寫時復(fù)制”(copy-on-write
)
上述代碼為什么會觸發(fā)的“寫時復(fù)制”?
Circle
是結(jié)構(gòu)體類型翼馆,也就是值類型割以,所以circle
和circle1
并不共享狀態(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
類型建蹄,也就是引用類型,所以circle
和circle1
共享狀態(tài)裕偿。當(dāng)circle1.radious
發(fā)生改變洞慎,circle
也會隨之改變