跳转至

协议与继承

class定义了对象是什么

protocol定义了对象有哪些(共同)行为 ,并通过 extension 的方式来对我们的类进行扩展。

//CustomStringConvertible是debug下的一个协议属性
class LGTeacherP{
    var age = 10
    var name = "Kody"
    func debug(){
        print(".....")
    }
}
class Dog{
    var name = "糯米"
    var type = "白梗"
    func debug(){
        print(".....")
    }
}

extension LGTeacherP: CustomStringConvertible{
    var description: String { get { return "LGTeacher: \(age)\(name)"}}
}
extension Dog: CustomStringConvertible{
    var description: String { get { return "Dog: \(name)\(type)"}}
}

func print(subject: CustomStringConvertible) {//subject遵循CustomStringConvertible协议
    let string = subject.description
    print(string)
    //to do...
}

func test() {
    print(subject: LGTeacherP())
}

协议中定义属性

协议要求属性必须明确是 getgetset

protocol HHProtocol{
    var age: Int{ get set }
    var name: String{ get }
}

//需要注意的一点是:并不是说当前声明 get 的属性一定是计算属性 
class HHTeacherP: HHProtocol{
    var age: Int = 18
    var name: String//name可以是存储属性
    init(_ name: String) {
        self.name = name
    }
}

协议中的异变方法

表示在该方法可以改变其所属的实例,以及该实例的所有属性(用于枚举和结构体),在为类实现该方法的时候不需要写 mutating 关键字。

枚举和结构体协议中的方法需要mutating,类不需要mutating。

protocol Togglable {
  mutating func toggle()
}

协议中定义初始化器

类在实现协议中的初始化器必须使用 required 关键字修饰初始化器,表明所有该类的子类都必须实现该初始化器。

protocol HHClassProtocol {
    init(_ age: Int)
}
class HHTe: HHClassProtocol{
    var age = 10
    required init(_ age: Int) {//必须加require关键字
        self.age = age
    }
}

//final不允许继承,就可以不加require关键字
final class HHTeF: HHClassProtocol{
    var age = 10
    init(_ age: Int) {
        self.age = age
    }
}

类专用协议

通过添加 AnyObject 关键字到协议的继承列表,就可以限制协议只能被类类型采纳。这样才delegate属性可以用weak修饰。

protocol MyProtocol: AnyObject{}

可选协议

如果不想强制让遵循协议的类类型实现,可以使用 optional 作为前缀放在协议的定义。

@objc protocol IncrementableO{
    @objc optional func increment(by: Int)
}
class HHTeaO: IncrementableO{
}
func testO() {
    let t:IncrementableO = HHTeaO()
    t.increment?(by: 10)
}

协议和继承

protocol Incrementable{
//    func increment(by: Int)
}
extension Incrementable{
    func increment(by: Int) {
        print("协议extension实现")
    }
}
class HHTea: Incrementable{
    //PWT
    func increment(by: Int) {
        print("HHTea协议实现")
    }
}
class SubHHTea: HHTea{
    //子类没有实现协议方法,没有协议见证表
}

func test2() {
    let t:Incrementable = SubHHTea()
    t.increment(by: 10)//执行的是HHTea的increment方法 如果protocol Incrementable没有定义increment方法,那么执行的是extension Incrementable的increment方法
}

协议原理探究

// MARK: 案例
protocol Shape{
    var area: Double{
        get
    }
}
//前16字节加8字节 = 24
class Circle: Shape{
    var radious: Double
    init(_ radious: Double) {
        self.radious = radious
    }
    var area: Double{
        get{
            return radious * radious * 3.14
        }
    }
}
func testShape() {
    let circle: Circle = Circle.init(10.0)
    print(class_getInstanceSize(Circle.self))
    print(MemoryLayout.size(ofValue: circle))//打印8

    var circle1: Shape = Circle.init(10.0)
    print(MemoryLayout.size(ofValue: circle1))//打印40
    withUnsafePointer(to: &circle1) { ptr in
        ptr.withMemoryRebound(to: HHProtocolBox.self, capacity: 1) { pointer in
            print(pointer.pointee)
            let descPtr = pointer.pointee.witness_table.pointee.protocol_conformance_descriptor.pointee.protocolDesc.getmeasureRelativeOffset()
            print(String(cString: descPtr.pointee.Name.getmeasureRelativeOffset()))
            print(pointer.pointee.witness_table.pointee.witnessMethod)
            print("end")
        }
    }
}



// MARK: protocol底层结构体
//Existential Container 是编译器生成的一种特殊的数据类型,用于管理遵守了相同协议的协议类型,因为这些类型的内存大小不一致,所以通过当前的 Existential Container 统一管理
struct HHProtocolBox{
    var heapObject: UnsafeRawPointer
    var unknow1: UnsafeRawPointer
    var unknow2: UnsafeRawPointer
    var metadata: UnsafeRawPointer
    var witness_table: 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>
}

// 还原TargetRelativeDirectPointer
// 相对地址信息
struct TargetRelativeDirectPointer<Pointee>{
    var offset: Int32

    mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset

        return withUnsafePointer(to: &self) { p in
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
        }
    }
}
  • 每个遵守了协议的类,都会有自己的PWT,遵守的协议越多,PWT中存储的函数地址就越多
  • PWT的本质是一个指针数组,第一个元素存储TargetProtocolConformanceDescriptor,其后面存储的是函数地址
  • PWT的数量与协议数量一致
  • Existential Container 是编译器生成的一种特殊的数据类型,用于管理遵守了相同协议的协议类型,因为这些类型的内存大小不一致,所以通过当前的 Existential Container 统一管理
  • 对于小容量的数据,直接存储在 Value Buffer
  • 对于大容量的数据,通过堆区分配,存储堆空间的地址

witness-table协议见证表protocol with table

类遵循了协议,实现了方法,编译器就会为类创建witness-table,记录类实现方法的编码信息。为每一个遵循协议的类创建witness-table。

两个类继承一个协议,两个协议见证表。

类遵循两个协议,PWT有两个。遵循的协议中的方法越多,PWT中的函数就越多。

NSObjectProtocol

Protocol继承NSObjectProtocol,才可以判断

delegate?.responds(to: #selector(<#T##@objc method#>))
@objc protocol HHGroupShadowTableViewDelegate: NSObjectProtocol {
    func groupShadowTableView(_ tableView: HHGroupShadowTableView, heightForFooterInSection section: Int) -> CGFloat
}


func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
  if let delegate = self.groupShadowDelegate, delegate.responds(to: #selector(HHGroupShadowTableViewDelegate.groupShadowTableView(_:heightForFooterInSection:))) {
    return delegate.groupShadowTableView(self, heightForFooterInSection: section)
  }
  return 0.0
}