协议方法的调度
protocol Incrementable{
var age: Int {set get}
func increment(by: Int)
}
extension Incrementable {
func increment(by: Int) {
print("extension")
}
}
class LGTeacher: Incrementable {
var age = 10
func increment(by: Int) {
print(by)
}
}
指定具体类型LGTeacher,方法从v-Table查找调用
var t: LGTeacher = LGTeacher()
t.increment(by: 10)
.sil文件
%15 = class_method %9 : $LGTeacher, #LGTeacher.increment : (LGTeacher) -> (Int) -> (), $@convention(method) (Int, @guaranteed LGTeacher) -> () // user: %16
sil_vtable LGTeacher {
#LGTeacher.increment: (LGTeacher) -> (Int) -> () : @$s4main9LGTeacherC9increment2byySi_tF // LGTeacher.increment(by:)
}
class_method在sil语法,从vtable 中调用
指定协议类型
var t: Incrementable = LGTeacher()
t.increment(by: 10)
.sil文件
witness_method $@opened("B21A40CE-803D-11EC-8631-9801A7B32FB9") Incrementable, #Incrementable.increment :
(Self) -> (Int) -> ()
vtable方法函数表
sil_vtable LGTeacher {
#LGTeacher.increment: (LGTeacher) -> (Int) -> () : @$s4main9LGTeacherC9increment2byySi_tF // LGTeacher.increment(by:)
}
witness_table 协议见证表PWT
sil_witness_table hidden LGTeacher: Incrementable module main {
method #Incrementable.increment:
(Self) -> (Int) -> () : @$s4main9LGTeacherCAA13IncrementableA2aDP9increment2byySi_tFTW // protocol witness for Incrementable.increment(by:) in conformance LGTeacher }
witness_table中Incrementable.increment的实现
// protocol witness for Incrementable.increment(by:) in conformance LGTeacher
sil private [transparent] [thunk] [ossa] @$s4main9LGTeacherCAA13IncrementableA2aDP9increment2byySi_tFTW : $@convention(witness_method: Incrementable) (Int, @in_guaranteed LGTeacher) -> () {
...
%3 = class_method %2 : $LGTeacher, #LGTeacher.increment : (LGTeacher) -> (Int) -> (), $@convention(method) (Int, @guaranteed LGTeacher) -> () // user: %4
...
}
class_method 会调度vtable
指定协议类型
var t: Incrementable = LGTeacher()
方法调度过程:witness_table -> class_method -> sil_vtable 找到最终类的实现
协议类型的内存结构
协议类型是占用40个字节
第一个8字节代表储存值的堆地址空间
第4个字节:具体实现类的metadata地址
第5个字节:witness_table(PWT)的地址
witness_table(PWT)的探究
协议类型是占用40个字节,40个字节是Existential Container
Existential Container 是编译器生成的一种特殊的数据类型,用于管理遵守了相同协议的协
议类型,因为这些类型的内存大小不一致,所以通过当前的 Existential Container 统一管理
探究Existential Container中的witness_table
生成.ll文件
efine i32 @main(i32 %0, i8** %1) #0 {
entry:
...
store i8** getelementptr inbounds ([5 x i8*], [5 x i8*]* @"$s4main9LGTeacherCAA13IncrementableAAWP", i32 0, i32 0), i8*** getelementptr inbounds (%T4main13IncrementableP, %T4main13IncrementableP* @"$s4main1tAA13Incrementable_pvp", i32 0, i32 2), align 8
...
终端还原s4main9LGTeacherCAA13IncrementableAAWP
xcrun swift-demangle s4main9LGTeacherCAA13IncrementableAAWP
$s4main9LGTeacherCAA13IncrementableAAWP ---> protocol witness table for main.LGTeacher : main.Incrementable in main
s4main9LGTeacherCAA13IncrementableAAWP是witness_table(PWT)
.ll文件
@"$s4main9LGTeacherCAA13IncrementableAAWP" = hidden constant [5 x i8*] [i8* bitcast (%swift.protocol_conformance_descriptor* @"$s4main9LGTeacherCAA13IncrementableAAMc" to i8*), i8* bitcast (i64 (%T4main9LGTeacherC**, %swift.type*, i8**)* @"$s4main9LGTeacherCAA13IncrementableA2aDP3ageSivgTW" to i8*)
s4main9LGTeacherCAA13IncrementableA2aDP3ageSivgTW终端还原
xcrun swift-demangle s4main9LGTeacherCAA13IncrementableA2aDP3ageSivgTW
$s4main9LGTeacherCAA13IncrementableA2aDP3ageSivgTW ---> protocol witness for main.Incrementable.age.getter : Swift.Int in conformance main.LGTeacher : main.Incrementable in main
由此推断
PWT的本质是一个指针数组,第一个元素存储TargetProtocolConformanceDescriptor,其后面存储的是函数地址
每个遵守了协议的类,都会有自己的PWT,遵守的协议越多,PWT中存储的函数地址就越多
PWT的数量与协议数量一致
struct LGProtocolBox {
var heapPointer: UnsafeRawPointer
var valueBuffer2: UnsafeRawPointer
var valueBuffer3: 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>
}
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))
}
}
}
withUnsafePointer(to: &t){ptr in
ptr.withMemoryRebound(to:LGProtocolBox.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")
}
}
探究Existential Container的值类型的储存情况
对于小容量的数据,直接存储在 Value Buffer
对于大容量的数据,通过堆区分配,存储堆空间的地址
小容量的数据(24字节),直接存储在 Value Buffer
protocol Shape {
var radious: Double {get set}
var width: Double {get set}
var height: Double {get set}
// var height_2: Double {get set}
}
struct Circle: Shape{
var radious: Double
var width: Double = 20
var height: Double = 30
// var height_2: Double = 40
init(_ radious: Double) {
self.radious = radious
}
}
var circle: Shape = Circle(10.0)
var circle2: Shape = circle
circle2.width = 50.0
print("end")
大容量的数据,通过堆区分配,存储堆空间的地址
protocol Shape {
var radious: Double {get set}
var width: Double {get set}
var height: Double {get set}
var height_2: Double {get set}
}
struct Circle: Shape{
var radious: Double
var width: Double = 20
var height: Double = 30
var height_2: Double = 40
init(_ radious: Double) {
self.radious = radious
}
}
var circle: Shape = Circle(10.0)
var circle2: Shape = circle
circle2.width = 50.0
print("end")
可以看出是堆区分配,存储堆空间的地址
存储堆空间的地址发生改变
赋值时重新复制了一份,写时复制(根据引用计数进行判断)
类型擦除
protocol DataFetch {
associatedtype dataType
func fetch(completion: ((Result<dataType, Error>) -> Void)?)
}
struct User {
let userId: Int
let name: String
}
struct UserData: DataFetch {
typealias dataType = User
func fetch(completion: ((Result<User, Error>) -> Void)?) {
let user = User(userId: 1001, name: "Jack")
completion?(Result.success(user))
}
}
struct VIPFetch: DataFetch {
typealias dataType = User
func fetch(completion: ((Result<User, Error>) -> Void)?) {
let user = User(userId: 1002, name: "VIP")
completion?(Result.success(user))
}
}
引入一个中间层来解决ViewController 和 UserData 对象之间的依赖关系
定义一个中间层结构体 AnyDataFetch , AnyDataFetch 实现了 DataFetch 的所有方法。
1.在 AnyDataFetch 的初始化过程中,实现协议的类型会被当做参数传入(依赖注入)
2.在 AnyDataFetch 实现的具体协议方法 fetch 中,再转发实现协议的抽象类型
struct AnyDataFetch<T>: DataFetch {
typealias dataType = T
private let _fetch: (((Result<T, Error>) -> Void)?) -> ()
init<U: DataFetch>(_ fetchable: U) where U.dataType == T {
_fetch = fetchable.fetch
}
func fetch(completion: ((Result<T, Error>) -> Void)?) {
_fetch(completion)
}
}
class SomeViewController<T>{
let userData: AnyDataFetch<T>
init(userData: AnyDataFetch<T>) {
self.userData = userData
}
}
func getDataFectchContent<U: DataFetch>(_ dataFetch: U) {
// let userData = UserData()
let anyDataFetch = AnyDataFetch<U.dataType>(dataFetch)
let vc = SomeViewController(userData: anyDataFetch)
vc.userData.fetch { result in
switch result {
case .success(let user):
if let use = user as? User {
print("id: \(use.userId), name: \(use.name)")
}
case .failure(let error):
print(error)
}
}
}
let userData = UserData()
getDataFectchContent(userData)
let vipFetch = VIPFetch()
getDataFectchContent(vipFetch)
AnySequence使用案例
CustomDataSeuqence遵循协议的类型可以进行遍历
struct LGTeacher: CustomDataSeuqence {
let name: String
let age: Int
}
struct VIP: CustomDataSeuqence {
var VIPDate: String
var VIPName: String
var VIPLevel: Int
}
VIP 和 USer 来说他们的行为是一致的,所以抽象出一个统一的协议
protocol CustomDataSeuqence: Sequence { }
extension CustomDataSeuqence {
func makeIterator() -> CustomIteratorProtocol {
return CustomIteratorProtocol(self)
}
}
struct CustomIteratorProtocol: IteratorProtocol {
typealias Element = String
var children: Mirror.Children
init(_ obj: Any) {
let mirror = Mirror(reflecting: obj)
children = mirror.children
}
mutating func next() -> String? {
guard let item = children.popFirst() else {
return nil
}
return item.label
}
}
let t = LGTeacher(name: "Tom", age: 27)
let vip = VIP(VIPDate: "2022", VIPName: "IOS", VIPLevel: 8)
let users = [AnySequence(t), AnySequence(vip)]
//let users: [CustomDataSeuqence] = [t, vip] 编译报错
for item in users {
for mod in item {
print(mod)
}
}
泛型探究
函数泛型被调用时,会传入具体的类型,并从值目击表中取size,分配内存大小
func test<T>(_ value: T) -> T {
let temp = value
return value
}
test(10)
.ll文件
define hidden swiftcc void @"$s4main4testyxxlF"(%swift.opaque* noalias nocapture sret %0, %swift.opaque* noalias nocapture %1, %swift.type* %T) #0 {
entry:
...
%3 = bitcast %swift.type* %T to i8***
%4 = getelementptr inbounds i8**, i8*** %3, i64 -1
%T.valueWitnesses = load i8**, i8*** %4, align 8, !invariant.load !35, !dereferenceable !36
...
}
赋值valueWitnesses 值目击表
valueWitnesses的地址是 %swift.type*向前偏移i8*的长度
.ll文件查到
wift.vwtable = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i64, i64, i32, i32 }
解析成结构体
struct ValueWitnessesTable {
var initializeBufferWithCopy: UnsafeRawPointer
var destroy: UnsafeRawPointer
var initialzeWithCopy: UnsafeRawPointer
var assignWithCopy: UnsafeRawPointer
var initializeWithTake: UnsafeRawPointer
var assignWithTake: UnsafeRawPointer
var getEnumTagSinglePayload: UnsafeRawPointer
var storeEnumTagSinglePayload: UnsafeRawPointer
var size: Int
var stride: Int
var flags: Int
}
struct TargetMeatadata {
var kind: Int
}
struct LGTeacher {
var age = 10
var age1 = 20
}
let ptr = unsafeBitCast(LGTeacher.self as Any.Type , to: UnsafeMutablePointer<TargetMeatadata>.self)
let valueWitnessesTable = UnsafeRawPointer(ptr).advanced(by: -MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: UnsafeMutablePointer<ValueWitnessesTable>.self).pointee
print(valueWitnessesTable.pointee) size 16
print("end")
valueWitnessesTable 值目击表储存 size stride 一些函数
泛型是引用类型,会包装成统一的结构体