协议的基本⽤法
协议的语法格式
protocol MyProtocol{
//body
}
我们熟悉的 class
, struct
, enum
都可以遵循协议,如果要遵守多个协议,使⽤逗号分隔。
struct Teacher: Protocol1, Protocol2{
//body
}
这⾥特别说⼀下,如果 class
中有 superClass
,⼀般我们放在遵循的协议之前
class Teacher: NSObject, Protocol1{
//body
}
协议中可以添加属性,⽤⼏个点需要注意⼀下:
- 协议同时要求⼀个属性必须明确是可读的或可读的和可写的
- 属性要求定义为变量属性
protocol MyProtocol{
var age:Int {get}
var time:Int{get set}
}
class myClass: MyProtocol {
var time: Int = 0
var age: Int {
get{
time * time
}
}
}
在协议中定义⽅法,我们只需要定义当前⽅法的名称,参数列表和返回值
protocol MyProtocol{
func doSomething()
//可能被结构体或者类继承
static func teach()
}
class myClass: MyProtocol {
func doSomething() {
print("doSomething")
}
static func teach() {
print("teach")
}
}
协议中也可以定义初始化⽅法,当我们实现初始化器的时候,必须使⽤ required 关键字
protocol MyProtocol{
init(age:Int)
}
class myClass: MyProtocol {
required init(age: Int) {
print(age)
}
}
如果想要协议只能被类准守,需要实现AnyObject
protocol MyProtocol:AnyObject
将协议作为类型
- 作为函数,⽅法或初始化程序中的参数类型或返回类型
- 作为常量,变量或属性的类型
- 作为数组,字典或其他容器中项⽬的类型
protocol MyProtocol {
func teach()
}
extension MyProtocol {
func teach() { print("MyProtocol") }
}
class Teacher: MyProtocol {
func teach() { print("MyClass") }
}
let object: MyProtocol = Teacher()
object.teach()
let object1: Teacher = Teacher()
object1.teach()
···············
MyClass
MyClass
通过sil文件可以看到
%10 = witness_method $@opened("5DA67B08-48DE-11EB-AA97-ACDE48001122") MyProtocol, #MyProtocol.teach!1 : <Self where Self : MyProtocol> (Self) -> () -> (), %9 : $*@opened("5DA67B08-48DE-11EB-AA97-ACDE48001122") MyProtocol : $@convention(witness_method: MyProtocol) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9; user: %11
%11 = apply %10<@opened("5DA67B08-48DE-11EB-AA97-ACDE48001122") MyProtocol>(%9) : $@convention(witness_method: MyProtocol) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9
// protocol witness for MyProtocol.teach() in conformance Teacher
sil private [transparent] [thunk] @$s4main7TeacherCAA10MyProtocolA2aDP5teachyyFTW : $@convention(witness_method: MyProtocol) (@in_guaranteed Teacher) -> () {
// %0 // user: %1
bb0(%0 : $*Teacher):
%1 = load %0 : $*Teacher // users: %2, %3
%2 = class_method %1 : $Teacher, #Teacher.teach!1 : (Teacher) -> () -> (), $@convention(method) (@guaranteed Teacher) -> () // user: %3
%3 = apply %2(%1) : $@convention(method) (@guaranteed Teacher) -> ()
%4 = tuple () // user: %5
return %4 : $() // id: %5
} // end sil function '$s4main7TeacherCAA10MyProtocolA2aDP5teachyyFTW'
sil_vtable Teacher {
#Teacher.teach!1: (Teacher) -> () -> () : @$s4main7TeacherC5teachyyF // Teacher.teach()
#Teacher.init!allocator.1: (Teacher.Type) -> () -> Teacher : @$s4main7TeacherCACycfC // Teacher.__allocating_init()
#Teacher.deinit!deallocator.1: @$s4main7TeacherCfD // Teacher.__deallocating_deinit
}
sil_witness_table hidden Teacher: MyProtocol module main {
method #MyProtocol.teach!1: <Self where Self : MyProtocol> (Self) -> () -> () : @$s4main7TeacherCAA10MyProtocolA2aDP5teachyyFTW // protocol witness for MyProtocol.teach() in conformance Teacher
}
witness_method
: 通过PWT
(协议⽬击表)获取对应的函数地址.
在main函数中通过witness_table
调用继承协议的实现找到vtable
如果把协议中的声明去掉
protocol MyProtocol {
//func teach()
}
extension MyProtocol {
func teach() { print("MyProtocol") }
}
class Teacher: MyProtocol {
func teach() { print("MyClass") }
}
let object: MyProtocol = Teacher()
object.teach()
let object1: Teacher = Teacher()
object1.teach()
···············
MyProtocol
MyClass
再次查看sil
// function_ref MyProtocol.teach()
%10 = function_ref @$s4main10MyProtocolPAAE5teachyyF : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // user: %11
%11 = apply %10<@opened("E3E7015A-48DF-11EB-8960-ACDE48001122") MyProtocol>(%9) : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9
直接调用了协议里的方法
PWT
到底存放在哪里呢
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 circle:Shape = Circle.init(10.0)
swiftc -emit-ir main.swift | xcrun swift-demangle > ./main.ll && open main.ll
//操作的都是这个结构体
%T4main5ShapeP = type { [24 x i8], %swift.type*, i8** }
//将metadata放入
store %swift.type* %4, %swift.type** getelementptr inbounds (%T4main5ShapeP, %T4main5ShapeP* @"main.circle : main.Shape", i32 0, i32 1), align 8
//将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.circle : main.Shape", i32 0, i32 2), align 8
//将heapObject放入
store %T4main6CircleC* %5, %T4main6CircleC** bitcast (%T4main5ShapeP* @"main.circle : main.Shape" to %T4main6CircleC**), align 8
用swift来模拟一下
protocol Shape{
var area: Double{ get }
}
struct Circle: Shape{
var radious: Double
var radious1: Double = 20
var radious2: Double = 30
// var radious3: Double = 40
init(_ radious: Double) {
self.radious = radious
}
var area: Double{
get{
return radious * radious * 3.14
}
}
}
var circle:Shape = Circle.init(10.0)
//24字节
struct protocolData {
var value1:UnsafeRawPointer
var value2:UnsafeRawPointer
var value3:UnsafeRawPointer
var type:UnsafeRawPointer
var pwt:UnsafeRawPointer
}
withUnsafePointer(to: &circle, { ptr in
ptr.withMemoryRebound(to: protocolData.self, capacity: 1, {point in
print(point.pointee)
})
})
·················
protocolData(value1: 0x4024000000000000, value2: 0x4034000000000000, value3: 0x403e000000000000, type: 0x0000000100003098, pwt: 0x0000000100003028)
通过实现可以发现如果我们在结构体Circle
声明三个变量,就会分配存放在value1
,value2
,value3
中,也就是valuebuffer
中,超过3个属性,就会在value
中开辟一个内存空间进行存放
网友评论