函数的调用机制
 
函数的调用机制是在函数调用时通过那种路径走到最终调用函数地址的机制。
在编程语言中,函数的调用机制有三种
1.静态调用:编译期就确定了函数内存地址,执行效率最高,还可以使用编译器优化如:inline函数内联提升执行效率。缺点:因为函数调用的内存地址在编译期已经确定,则无法支持继承等动态修改调用的方式。
2.函数表调用:每个类都有一份自己的v-table虚函数表,里面是以函数名为key, 函数地址为value; 如果子类override了父类的方法,那么这个方法名key对应的value就是那个子类重写的新的函数地址。
3.消息发送调用:所有的函数调用最后都会转换成一系列参数,通过消息发送的方式进行调用。这种方式最灵活,可以override重写父类方法,可以swizzle交互类中方法的实现,可以swizzle_isa修改类的父类即:修改继承链。
 
常见语言的调用机制
C++:默认使用静态调用机制,可以通过virtural修饰成使用函数表机制调用。
Java:默认使用函数表调用机制,可以通过final修饰成直接调用机制。
Object-C:只能使用消息发送的方式进行方法调用,可以使用C代码写直接调用机制的代码。
Swift:可以根据不同的修饰符,根据情况使用上面三种方式的任一方式。
Swift中的函数调用方式
Swift的调用方法非常灵活,它三种类型都支持。
首先在大的分类上分2种:Static Dispatch 和 Dynamic Dispatch。
Static Dispatch是静态调用,调用方法的函数地址是在编译时确定的。
Dynamic Dispatch是动态调用,调用的函数地址要在运行时才能确定。
Dynamic Dispatch动态调用又可以分为3个子类:V-Table Dispatch, witness table dispatch, objc_msgSend。
Swift的类型分2种,值类型和引用类型。值类型包含结构体和枚举,对于值类型中的方法调用基本都是静态调用,执行效率非常高。
引用类型就是对象,Swift中的类型分为是否继承自NSObject, 原因是如果继承NSObject,那么对象的结构体中(类的底层实现也是一个结构体)就有了OC运行时中那一套的所有机制,isa指针,方法列表,属性列表,协议列表等。从而函数调用就支持消息发送方式了。这也是Swift类中方法走消息发送的前提条件。
值类型-Struct
1.因为结构体不能继承,所以它的struct下定义的方法的调用都是静态的。
2.它的extense下扩展的方法不能被override,走的也是静态的。
3.调用遵守协议的方法实现就是自己定义的方法一样。
引用类型-纯Swift类
1.在Swift类中定义的方法,影响它调用方式的只有final关键字,正常定义的方法Swift通过一种Virtual Table的机制在运行时寻找方法的内存地址并调用。被final修饰的方法不能被override,走静态调用。
2.它的extense下扩展的方法不能被override,走静态调用。
3.调用遵守协议的方法实现就是自己定义的方法一样,走V-Table虚表调用。
引用类型-Swift类继承自NSObject
这种方式创建的Swift类,调用方式受关键字影响比较大。
1.被final修饰的,走静态调用,因为不能被override。
2.类中定义的普通方法和被@objc修饰的方法,都是走的V-Table方式调用。NSObject的子类+@objc修饰符的方法,只能表示可以让OC类进行调用,但真正的执行机制还是走的V-Table虚表调用。
3.@objc+dynamic修饰的方法走OC的runtime消息发送。
4.Extense下的方法默认走静态调用,因为不能被override。如果此时被@objc和dynamic修饰,就无法走静态,走的OC的runtime消息发送。
5.调用遵守协议的方法实现就是自己定义的方法一样。
上面是在没有做编译器优化的情况下,如果做了编译器优化,则编译器会尽可能走静态调用的方式,提高运行效率。
Protocal
1.当变量以当前对象的方式调用时,走的是当前对象定义方法的方式。
2.当变量以Protocal协议对象的方式调用时,走的是Witness Table
3.协议中被@objc修饰的方法,走的是runtime的消息发送

判断函数是以哪种方式调用的方法
静态调用
因为静态调用时,call函数的地址是固定的,根据machO文件加载到内存的内存分布

所以,静态调用时,函数地址是一个固定的,比栈变量内存地址小的内存地址。
消息发送
消息发送时,函数调用通常都会走到同一段函数内存地址中,因为所有的函数调用都是使用同一个消息发送方法进行的。
函数表调用
函数调用的实际内存地址通常需要根据偏移量动态计算而来, 不像静态调用和消息发送在汇编代码里有明显的特征。

可以通过汇编调试进行佐证上面的函数调用方式
查看寄存器中的值
register read/格式
register read/x register write 寄存器名称 数值
register write rax 0

查看具体内存地址中的值

x/数值-格式-字节大小 内存地址
x/3xw 0x000010 memory write 内存地址 数值
memory write 0x000010 10
Swift和OC在模拟器调试时,采用的汇编类型是AT&T
常用寄存器说明:
rax寄存器常用于函数传参和返回值
rbp,rsp寄存器常用于栈数据的读取
rip常用于指令寄存器
常用指令说明:
movq $30 %rax  //将30数据放置在rax寄存器中
leaq -0x86(%rbp) %rax //将%rbp-0x86中的内存地址放置在rax寄存器中
call -0x86(%rbp) //函数调用,下面通常有ret命令对应
jump -0x86(%rbp) //if跳转
常见表示说明:
0x4bdc(%rip),一般是全局变量,全局区(数据段)
-0x78(%rbp), 一般是局部变量,栈空间
0x10(%rax), 一般是堆空间
参考文章:
https://zhuanlan.zhihu.com/p/35696161
https://www.cnblogs.com/zhou--fei/p/17245908.html
 
 

Swift函数调用方式浅析的更多相关文章

  1. (转)函数调用方式与extern "C"

    原文:http://patmusing.blog.163.com/blog/static/13583496020103233446784/ (VC编译器下) 1. CALLBACK,WINAPI和AF ...

  2. 函数调用方式__stdcall、__cdel

    函数调用方式关系对比如下: 关键字 调用规则 参数传递方向 返回 参数寄存器 堆栈的清除 __cdecl C语言 从右向左 EAX 无 调用者 __stdcall Win32标准  从右向左 EAX ...

  3. JavaScript中七种函数调用方式及对应 this 的含义

    this 在 JavaScript 开发中占有相当重要的地位,不过很多人对this这个东西都感觉到琢磨不透.要真正理解JavaScript的函数机制,就非常有必要搞清楚this到底是怎么回事. 函数调 ...

  4. 前端JS面试题汇总 Part 3 (宿主对象与原生对象/函数调用方式/call与apply/bind/document.write)

    原文:https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questio ...

  5. jQuery内部原理和实现方式浅析

    这篇文章主要介绍了jQuery内部原理和实现方式浅析,本文试图从整体来阐述一下jQuery的内部实现,需要的朋友可以参考下 这段时间在学习研究jQuery源码,受益于jQuery日益发展强大,研究jQ ...

  6. 函数调用方式--__thiscall调用方式和__cdecl,__stdcall有什么区别

    函数调用方式--__thiscall调用方式和__cdecl,__stdcall有什么区别 首先,__thiscall是关于类的一种调用方式,它与其他调用方式的最大区别是:    __thiscall ...

  7. 【Python】利用递归函数调用方式,将所输入的字符串,以相反的顺序显示出来

    源代码: """ 利用递归函数调用方式,将所输入的字符串,以相反的顺序显示出来 string_reverse_output():反向输出字符串的自定义函数 pending ...

  8. JavaScript系列:函数调用方式

    有关JS的问题,持续更新.. 一,函数调用的4种方式 1,函数调用模式 //下面这种模式叫 “函数调用模式”:窗后window来调用 //函数调用四种方式的基础 //这tm不就是作用域this的问题吗 ...

  9. C++对象模型的那些事儿之六:成员函数调用方式

    前言 C++的成员函数分为静态函数.非静态函数和虚函数三种,在本系列文章中,多处提到static和non-static不影响对象占用的内存,而虚函数需要引入虚指针,所以需要调整对象的内存布局.既然已经 ...

  10. Swift 函数调用到底写不写参数名

    最近真正开始学 Swift,在调用函数的时候遇到一个问题:到底写不写函数名? 我们来看两个个例子: // 1 func test(a: Int, b: Int) ->Int { return a ...

随机推荐

  1. [Wechat]概念辨析:微信的生态平台/运管平台

    0 引言 微信的各类XX社区.XX文档.XX平台,实在是太多,让人眼花缭乱.必须得理一理了. 1 微信公众平台 https://mp.weixin.qq.com/ 即 微信公众号(小程序 / 订阅号 ...

  2. Django简介 安装下载 app概念 主要目录介绍

    目录 Django简介 前戏 Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式,即模型M,视图V和模版T.这套框架是以比利时的吉普赛爵士吉他手Django Re ...

  3. JAVA注解@Scheduled 不执行

    spring boot项目需要在启动类加上注解 @EnableScheduling 定义一个接口 StockTask.java 1 public interface StockTask { 2 pub ...

  4. 阿里云交互式建模(DSW)的探索和踩坑

    前言 自己的笔记本炼丹还是太吃力了些,风扇嘶吼有点心疼,看到阿里云出了一些免费试用的资源,想着能白嫖一下高端显卡跑一跑自制模型还挺有趣,于是有了下面的一些操作,其实没啥难度的,大胆的按文档来做基本就可 ...

  5. [Pytorch框架] 4.1 Fine tuning 模型微调

    文章目录 4.1 Fine tuning 模型微调 4.1.1 什么是微调 为什么要微调 迁移学习 Transfer Learning 二者关系 4.1.2 如何微调 4.1.3 注意事项 4.1.3 ...

  6. 一条SQL如何被MySQL架构中的各个组件操作执行的?

    摘要:一条SQL如何被MySQL架构中的各个组件操作执行的,执行器做了什么?存储引擎做了什么?表关联查询是怎么在存储引擎和执行器被分步执行的?本文带你探探究竟! 本文分享自华为云社区<一条SQL ...

  7. Snort的安装——Fedora

    Snort Fedora Install 简介 Snort 是世界上知名的开源入侵防御系统 (IPS).Snort IPS 使用一系列规则来帮助定义恶意网络活动,并使用这些规则来查找与其匹配的数据包并 ...

  8. 第十四届蓝桥杯省赛C++ B组(个人经历 + 题解)

    参赛感受 这是我第一次参加蓝桥杯的省赛,虽然没什么参赛经验,但是自己做了很多前几届蓝桥杯的题,不得不说,这一届蓝桥杯省赛的难度相较于之前而言还是比较大的.之前很流行蓝桥杯就是暴力杯的说法,但是随着参赛 ...

  9. C# 实现窗体启动时隐藏

    在某些时候需要实现一个界面的后台程序,程序自动运行,但起初不显示窗体,在满足触发条件时显示,此时需要在运行程序时先自动隐藏窗体. 修改窗体对应的Program.cs: using System; us ...

  10. 数据库定时备份linux篇

    目录 1 序言 2 crond 相关知识点 2.1 crond 是什么? 2.2 crontab 进行定时任务设置 2.2.1 crontab 指令选项说明 2.2.2 crontab 指令使用格式 ...