swift-协议

一 协议与继承

class LGTeacher{
    var age = 10
    var name = "swift"
}

class LGStudent{
    var name = "oc"
    var score = 100
}

对于上面的两个类来说,如果需要为这两个类都添加一个debug函数,直观的方法是为每个都添加一个debug函数,但是当我们的代码中存在很多个这样的类来说显然是不合理的。从继承的角度来说,我们可以构建一个公共类,使得当前的类都继承自公共类,子类实现函数重载来达到目的,但是这种方式对于我们的代码来说具有较强的入侵性,那么我们还有什么办法呢,我们就可以通过一个协议来描述当前类的共同行为,,并使用 extension 方法来对我们的类进行扩展,这样就很棒。

extension LGTeacher:CustomStringConvertible{
    var description: String{get{return "LGTeacher"}}
}
extension LGStudent:CustomStringConvertible{
    var description: String{get{return "LGStudent"}}
}
func print(subject:CustomStringConvertible){
    let string = data.description
    //to do sth 
}

我们可以做一下总结:

  • class本质上定义了一个对象是什么,有什么属性以及行为
  • protocol本质上是约束类的共同行为

二、协议语法

接下来,我们来学习一下协议。

  • 协议的属性必须声明是get/get和set
  • 协议用 protocol 关键字
  • 协议内只负责方法的声明(方法名、属性、返回值),不做具体实现
  • 遵循协议的类需要实现相应的方法
  • 声明get的不一定是计算属性
protocol MyProtocol{
    var age : Int{get set}
    var name : String{get}
}
class LGTeacher{
    var age :Int = 18
    var name :String
    init(name:String){
        self.name = name
    }
}
  • 协议中的异变方法 mutating 表示该方法可以改变其所属的实例以及该实例的所有属性(用于枚举和结构体),在类中实现该方法时不需要加mutating关键字,结构体需要加
protocol Togglable{
    mutating func toggle()
}
class A:Togglable{
    func toggle() {
        
    }
}
struct B:Togglable{
    mutating func toggle() {
        
    }
}
  • 类在实现协议的初始化器中必须使用required关键字来修饰初始化器的实现(表示该类的所有子类都必须实现该初始化器),如果该类不需要被继承则不需要加关键字
protocol Incrementable{
    init(_name:String)
}
class A :Incrementable{
    var name : String = ""
    required init(_name: String) {
        self.name = _name
    }
}
  • 类专用协议可通过添加AnyObject关键字到协议的继承列表中,这样就可以限制该协议只可被类使用
protocol Incrementable:AnyObject{
    init(_name:String)
}
class A :Incrementable{
    var name : String = ""
    required init(_name: String) {
        self.name = _name
    }
}
  • 可选协议:如果噩梦不想强制遵循协议的类实现,可使用optional作为前缀放在协议中
@objc protocol Incrementable{
  @objc  optional func increment(by:Int)
}

三、协议原理探究

3.1 协议表(PWT-witness table)

protocol Incrementable{
    func incrementable(by:Int)
}
class A:Incrementable{
    var name:String = ""
    func incrementable(by: Int) {
        print("swift")
    }
}
var a = A()
a.incrementable(by: 10)
print("end")
  • class_method通过V-Table调度
  • witness_method通过witness_table调度

3.2 协议原理

protocol Incrementable{
    var radious:Double{get set}
}
class A:Incrementable{
    var radious: Double = 20.0
}
var a :Incrementable = A()
print("end")

通过分析IR代码,还原出结构体protocolBox

struct ProtocolBox {
    var valueBuffer1: UnsafeRawPointer
    var valueBuffer2: UnsafeRawPointer
    var valueBuffer3: UnsafeRawPointer
    var metadata: UnsafeRawPointer
    var witness_table: UnsafeMutablePointer<TargetWitnessTable>
}

我们尝试打印下面这个结构体:

protocol Incrementable{
    var radious:Double{get set}
}
struct A:Incrementable{
    var radious:Double = 20.0
    var width:Double = 20
}
class B:Incrementable{
    var radious: Double = 20.0
}
var a : Incrementable = A()
var b : Incrementable = B()
print("end")

 

 最终,我们通过源码分析TargetWitnessTable、TargetProtocolConformanceDescriptor、TaegetProtocolDescriptor得到最终结构:

 

 

 

自此,我们还原出协议的结构

struct OSProtocolBox {
    var heapObject: UnsafeRawPointer
    var unkonw1: UnsafeRawPointer
    var unkonw2: UnsafeRawPointer
    var metadata: UnsafeRawPointer
    var witnessTable: UnsafeMutablePointer<TargetWitnessTable>
}

struct TargetWitnessTable{
    var protocol_conformance_descriptor: UnsafeMutablePointer<TargetProtocolConformanceDescriptor>
    var witnessMethod: UnsafeRawPointer

}

struct TargetProtocolConformanceDescriptor{
    var protocolDesc: TargetRelativeDirectPointer<TargetProtocolDescriptor>
    var typeRef: UnsafeRawPointer
    var WitnessTablePattern: UnsafeRawPointer
    var flags: UInt32
}

struct TargetProtocolDescriptor
{
    var flags: UInt32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var Name: TargetRelativeDirectPointer<CChar>
    var NumRequirementsInSignature: UInt32
    var NumRequirements: UInt32
    var AssociatedTypeNames: TargetRelativeDirectPointer<CChar>
}

总结:

  • 每个遵守了协议的类,都会有自己的PWT,遵守的协议越多,PWT中存储的函数地址就越多
  • PWT的本质是一个指针数组,第一个元素存储TargetProtocolConformanceDescriptor,其后面存储的是函数地址PWT的数量与协议数量一致
  • Existential Container 是编译器生成的一种特殊的数据类型,用于管理遵守了相同协议的协 议类型,因为这些类型的内存大小不一致,所以通过当前的 Existential Container 统一管理
  • 对于小容量的数据,直接存储在 Value Buffe
  • r对于大容量的数据,通过堆区分配,存储堆空间的地址

 

posted on 2022-02-10 17:42  suanningmeng98  阅读(63)  评论(0编辑  收藏  举报