swift方法调度总结

方法调度

结论

  • Class中的方法
    • public open internal 方法调度都是函数派发方式
    • private fileprivate final 方法调度为静态派发方式
    • extension 中的方法都为静态派发方式
  • Struct中的方法
    • 全部都是静态派发调度方式: mutating extension public private...
  • Protocol中的方法
    • 方法最初定义在协议本身内, 则方法以协议函数表的方式调度
    • 方法最初定义在协议延展内, 则方法以静态派发的方式调度

验证Class中的方法调度

1、创建ClassPerson.swift原始文件。

class ClassPerson: NSObject {

    override init() {
        super.init()
        personFuncName1()
        personFuncName2()
        personFuncName3()
        personFuncName4()
        personFuncName5()
        personFuncName6()
        personFuncName7()
        personFuncName8()
    }
    
    dynamic func teach() {
        debugPrint(#function)
    }
    
    /// 消除函数调用后返回值未被使用的警告⚠
    /// 以前写法防止警告: _ = resultTest()
    @discardableResult func resultTest() -> Bool {
        return false
    }
    
    /// 函数表派发方式
    func personFuncName1() {
    }
    
    /// 函数表派发方式
    func personFuncName2() {
    }
    
    /// 加了 private 则为静态派发
    private func personFuncName5() {
    }
    /// 加了 final 则为静态派发
    final func personFuncName6() {
    }
    
    ///@objc dynamic 消息转发msgSend方式
    @objc dynamic func personFuncName7() {
    }
    
    @objc func personFuncName8() {
    }
}

/// 扩展里的方法都是静态派发方式
extension ClassPerson {
    /// swift5 方法替换 需要对被替换的方法加dynamic修饰
    @_dynamicReplacement(for: teach())
    private func teach1() {
        debugPrint(#function)
    }
    
    func personFuncName3() {
    }
    
    func personFuncName4() {
    }
}

2、编译sil文件
从终端进入到ClassPerson.swift目录下,在同级目录下生成sil文件。

// 编译 sil
swiftc -emit-sil Person.swift >> Person.sil
// 编译成带转译的 sil
swiftc -emit-sil Person.swift | xcrun swift-demangle >> Person.sil
// 编译成带转译的 ir
swiftc -emit-ir Person.swift | xcrun swift-demangle >> Person.ll

//其它
生成语法树: swiftc -dump-ast main.swift
生成最简洁的SIL中间代码:swiftc -emit-sil main.swift
生成LLVM的IR代码:swiftc -emit-ir main.swift -o main.ll
生成汇编代码:swiftc -emit-assembly main.swift -o main.s

转义后的sil文件能清晰的看出方法调用。

转义sil文件.png

如果不转义sil能否确定这就是personFuncName4()方法呢,使用下面命令行:

xcrun swift-demangle <混写后的名称>
real_function_name.png

function_ref

找到init初始化方法中对其它方法的调用。其中带有function_ref的就是静态派发调度方式。

function_ref.png

  • personFuncName3 personFuncName4 是扩展方法
  • personFuncName5private修饰的方法
  • personFuncName6final 修饰的方法

以上三种情况定义的方法都是静态派发调度方式。

断点汇编查看

xcode顶部导航栏选择Debug->Debug Workflow->Always Show Disassemebly,在init()方法最后打个断点,运行程序:

function_ref1.png

从汇编调试中明显看出方法personFuncName3 personFuncName4 personFuncName5 personFuncName6的调用都是直接访问函数地址的,说明在编译过程中就已经确定了函数地址,也就是静态派发调度方式了。

sil_vtable

sil_vtable.png

再看虚拟函数表中只有personFuncName1 personFuncName2 personFuncName5 personFuncName8 虽然personFuncName5在这表里面但是明细和其它不一样。这是因为它是private修饰的方法为静态派发调度方式。

@objc修饰的方法

@objc修饰的方法也是函数派发调度方式。在方法实现上看sil代码发现有两个实现,ClassPerson.personFuncName8() @objc ClassPerson.personFuncName8()并且第二个方法以静态派发方式调用了第一个方法。第二个方法就是暴露给oc调用的接口方法。

@objc.png

dynamic修饰的方法

我们用dynamic修饰了teach()方法,编译成sil代码后方法实现前有个[dynamically_replacable]字面意思就是动态可被替换的dynamic修饰的方法就是动态的可被替换,可被替换是指在OC运行时的方法交换的场景下可被替换。

dynamically_replaceable.png

@_dynamicReplacement(for: teach())

/// swift5 方法替换 需要对被替换的方法加dynamic修饰
    @_dynamicReplacement(for: teach())
    private func teach1() {
        debugPrint(#function)
    }

在swift5中进行dynamic修饰的方法替换。在编译的sil代码中可以查看到teach1()方法的实现就是替换了teach()方法。可以在sil_vtable中找到@$s6Person05ClassA0C5teachyyF这个指向的就是teach()方法。

dynamic_replacement_for.png

@objct dynamic修饰的方法

在上面init初始化方法调用中可以看到,调度方式是objc_method这是oc特有的方式-消息转发objc_msgSend

运行程序进入到汇编代码中就可以看到该方法是采用objc_msgSend方式调度

objc_msgSend.png

验证Struct中的方法调度

1、创建StructPerson.swift源文件

struct StructPerson {
    var name: String
    
    @discardableResult init(name: String) {
        self.name = name
        
        structFuncName1()
        
        structFuncName2()
        
        structFuncName3()
        
        structFuncName4()
    }
    
    func structFuncName1() {
        
    }
    
    mutating func structFuncName3() {
        name += #function
    }
    
    private func structFuncName4() {
        
    }
    
}

extension StructPerson {
    func structFuncName2() {
        
    }
}

2、编译成sil文件
找到init(name:)方法,查看里面的方法调用方式。可以看到不管是私有方法还是扩展里面的方法都是静态派发的方式function_ref

struct.png

验证Protocol中的方法调度

1、创建ProtocolPerson.swift源文件

protocol ProtocolPerson: NSObjectProtocol {
    func protocolFuncName1()
    func protocolFuncName2()
}

extension ProtocolPerson {
    
    func protocolFuncName3() {
        
    }
}


class ClassPersonBtn {
    weak var delegate: ProtocolPerson?
    
    func click() {
        delegate?.protocolFuncName1()
        delegate?.protocolFuncName2()
    }
  
}

class ClassPersonOwner: NSObject, ProtocolPerson {
    let btn = ClassPersonBtn()
    
    override init() {
        super.init()
        btn.delegate = self
        protocolFuncName3()
    }
    
    func protocolFuncName1() {
        
    }
    
    func protocolFuncName2() {
        
    }
}

2、编译成sil文件
protocolFuncName1 protocolFuncName2 这两个方法都是定义在协议内的,采用的都是函数派发调度方式。

protocol_class_method.png

protocolFuncName3这个方法是定义在协议扩展内的,采用的是静态派发方式。可以理解只要是方法是在extension中实现的都是采用静态派发方式调度。

protocol_function_ref.png

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

推荐阅读更多精彩内容