swift-- 协议

协议的基本⽤法

协议的语法格式

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声明三个变量,就会分配存放在value1value2value3中,也就是valuebuffer中,超过3个属性,就会在value中开辟一个内存空间进行存放

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容