解析调用过程与步骤 (解析调用过程包括)

解析调用过程与步骤详解 解析调用过程与步骤

一、引言

在软件开发和计算机编程领域,解析调用过程与步骤是不可或缺的一部分。
解析调用涉及一系列复杂的操作和步骤,以确保程序能够正确执行。
本文将详细解析解析调用过程与步骤,帮助读者更好地理解这一过程。

二、解析调用的概念

解析调用是指程序在执行过程中,对某个函数或方法进行调用的过程。
在这个过程中,程序需要找到被调用的函数或方法的定义,然后执行相应的代码。
解析调用的目的是实现代码的重用和模块化,提高程序的可维护性和可扩展性。

三、解析调用过程与步骤

1. 编译时解析调用

在编译时,编译器会对源代码进行解析,识别出所有的函数调用。
对于静态调用(即在编译时就能确定调用目标),编译器可以直接将调用转换为相应的机器指令。
而对于动态调用(如通过函数指针或变量调用的函数),编译器会在编译时保留这部分代码的符号信息,以便在运行时进行解析。

2. 运行时解析调用

在运行时,程序会加载相应的库或模块,将符号信息与实际函数地址进行绑定。
这个过程称为动态链接或加载。
当程序中的函数调用发生时,操作系统会根据符号信息找到相应的函数地址,并将调用跳转到该函数地址执行。
这就是动态解析调用的过程。

3. 解析调用的具体步骤

(1)程序加载:程序开始执行时,会加载相关的库和模块到内存中。
这些库和模块包含程序所需的各种函数和资源。

(2)符号解析:在程序运行过程中,当遇到函数调用时,编译器会检查符号表(symbol table),查找被调用函数的名称和对应的地址信息。
符号表是编译器在编译过程中生成的一种数据结构,用于记录程序中所有符号(如函数名、变量名等)的映射关系。

(3)动态链接:如果符号解析过程中找到了被调用函数的地址信息,那么程序会通过动态链接机制跳转到该函数地址执行。
动态链接是在运行时进行的链接过程,它允许程序在运行时加载和执行其他模块中的代码和数据。
如果找不到被调用函数的地址信息,则可能需要通过其他机制(如运行时库或操作系统提供的函数查找机制)来定位该函数。
如果找不到函数,则可能抛出错误或异常。

(4)函数调用执行:一旦找到被调用函数的地址并执行跳转,程序就会开始执行该函数内部的代码。
被调用函数可以访问调用函数的局部变量和全局变量,并可以修改它们的值。
当函数执行完毕后,程序会返回到调用点继续执行后续代码。

四、影响解析调用的因素及注意事项

解析调用的效率会受到多种因素的影响,如程序的结构、编译器的优化能力、操作系统和硬件性能等。为了提高解析调用的效率,需要注意以下几点:

1. 优化代码结构:合理的代码结构有助于提高编译器的优化能力,减少解析调用的开销。
2. 选择合适的编译器和工具链:不同的编译器和工具链对解析调用的处理方式有所不同,选择合适的编译器和工具链可以提高程序的性能。
3. 避免过度依赖动态链接库:过度依赖动态链接库可能导致程序启动缓慢和运行效率低下。在必要时可以考虑使用静态链接库来提高程序的性能。
4. 关注性能优化:对于性能要求较高的场景,需要对解析调用的性能进行优化,如使用性能分析工具分析函数调用过程,识别性能瓶颈并进行优化。在分析和优化过程中要注意保持代码的可读性和可维护性。还需要考虑线程安全和并发控制等问题,以确保在多线程环境下程序的正确运行。在涉及到操作系统和硬件层面的优化时还需要考虑跨平台兼容性和可移植性问题。因此在实际开发中应根据具体场景和需求进行相应的权衡和选择。五、总结通过本文对解析调用过程与步骤的详细解析我们可以了解到解析调用在软件开发和计算机编程中的重要性以及其在实现代码重用模块化提高程序可维护性和可扩展性方面的作用。同时我们也了解到解析调用过程中涉及的关键环节如编译时解析调用运行时符号解析动态链接等以及影响解析调用的因素和提高解析调用效率的方法。在实际开发中我们应结合具体场景和需求对解析调用进行优化以提高程序的性能和稳定性同时保持代码的可读性和可维护性确保程序的正确运行。


—— iOS 运行时中方法的调用流程

在iOS运行时系统中,调用方法的本质就是利用objc_msgSend进行消息发送:

iOS 中所有的类都是继承于 NSObject,一个对象所具有的方法分为实例方法和类方法,编译完成后的对象中,存在一个实例方法链表、一个缓存方法链表。当实例调用方法经objc_msgSend时:首先,在相应操做的对象中的缓存方法列表中找调用的方法,若找到,转向相应的实现并执行;若没找到,在对象的方法列表中查找,若是找到,转向相应的实现并执行;若是没找到,则递归的去父类指针所指向的类对象方法列表中查找;以此类推,若是一直到根类都没有找到,转向拦截调用,走消息转发机制;若是没有重写拦截调用方法,程序报错;

消息转发也被称为拦截调用,就是在找不到调用的方法后,且在程序崩溃以前,有机会经过重写NSObject的四个方法来补救处理:

若以上都不中,调用 NSObject 的 doesNotRecognizeSelector 方法抛出异常:

利用以上机制,可以对resolveInstanceMethod 和 resolveClassMethod 两个方法进行方法交换,拦截可能出现的 iOS 崩溃,然后自定义处理。

第一阶段,先征询接收者所属的类,是否需要动态的添加方法,用来处理当前未找到的方法。对象在无法解读消息时会首先调用所属类的下列类方法,来判断是否能接收消息:

例:

第二阶段,如果动态方法解析没有发现添加的方法,那么尝试转发给其他对象来处理这个方法。该步骤调用的方法是:

例:

第三阶段,如果没有可用的备选者,那么系统就会把消息所有相关内容封装成一个NSInvocation对象,再做最后的尝试,启动完整的消息转发。 先调用methodSignatureForSelector:获取方法签名,然后再调用forwardInvocation:进行处理,这一步的处理可以直接转发给其它对象,即和第二步的效果等效,但是很少有人这么干,因为消息处理越靠后,就表示处理消息的成本越大,性能的开销就越大。 所以,在这种方式下,一般会改变消息内容,比如增加参数,改变选择子等等,具体根据实际情况而定。

例:

这里就是利用了消息转发机制的第三个阶段,将NSIvocation分发给多个代理去响应。

由于OC的动态特性,在编译过程向类发送了其无法理解的消息并不会报错,因为在运行时,我们可以改变对象调用的方法、向类中添加方法。 只有当程序运行起来之后,才知道要真正执行哪个函数(动态绑定)。

OC消息发送原理、方法查找过程:

简单理解:

OC、运行时初始化时机:

参考文章:

iOS之使用NSInvocation调用方法

SOFARPC源码解析-服务调用

简介摘要 SOFARPC服务调用创建服务引用配置ConsumerConfig,自定义设置接口名称、调用协议、直连调用地址以及连接超时时间等基础配置;通过服务消费者启动类ConsumerBootstrap引用服务,客户端集群Cluster调用消费端调用器ConsumerInvoker实现Client发送数据给Server调用过程。 SOFARPC以基于Netty实现的网络通信框架SOFABolt用作远程通信框架,使用者不用关心如何实现私有协议的,直接使用内置RPC通信协议,启动客户端与服务端同时注册用户请求处理器即可完成远程调用: 1.调用方式 SOFARPC服务调用提供同步Sync、异步Future、回调Callback以及单向Oneway四种调用类型:

使用Future异步调用SOFABoot配置服务引用需要设置sofa:global-attrs元素的type属性声明调用方式为future:

如上设置为异步调用的方式。 客户端获取响应结果有两种方式: (1)通过 SofaResponseFuture直接获取结果。 第一个参数是获取结果的超时时间,第二个参数表示是否清除线程上下文中的结果。

(2)获取原生Futrue,该种方式获取JDK原生的Future,参数表示是否清除线程上下文中的结果。 因为响应结果放在JDK原生的Future,需要通过JDK Future的get()方法获取响应结果。

当前线程发起调用得到RpcResponseFuture对象,当前线程可以继续执行下一次调用。 在任意时刻使用RpcResponseFuture对象的get()方法来获取结果,如果响应已经回来此时就马上得到结果;如果响应没有回来则阻塞住当前线程直到响应回来或者超时时间到。 (3)Callback回调调用 客户端提前设置回调实现类,在发起调用后不会等待结果,是真正的异步调用,永远不会阻塞线程,结果处理是在异步线程里执行。 SOFA-RPC在获取到服务端的接口后会自动执行该回调实现,目前支持 bolt 协议。 客户端回调类需要实现接口:

如上设置是服务级别的设置,也可以进行调用级别的设置:

使用Callback回调调用SOFABoot配置服务引用需要设置sofa:global-attrs元素的type属性声明调用方式为callback,通过callback-ref属性声明回调的实现类,使用该服务引用发起调用时结果返回时由SOFARPC自动执行该回调类:

当前线程发起调用则本次调用马上结束执行下一次调用。 发起调用时需要注册回调,该回调需要分配异步线程池以待响应回来后在回调的异步线程池来执行回调逻辑。 (4)Oneway单向调用 客户端发送请求后不会等待服务端返回的结果,并且会忽略服务端的处理结果,目前支持bolt协议:

使用Oneway单向调用SOFABoot配置服务引用需要设置sofa:global-attrs元素的type属性声明调用方式为oneway:

当前线程发起调用后,不关心调用结果不做超时控制,只要请求已经发出就完成本次调用。 单向调用不关心响应结果,请求线程不会被阻塞,使用Oneway调用需要注意控制调用节奏防止压垮接收方。 注意Oneway调用不保证成功,而且发起方无法知道调用结果。 因此通常用于可以重试,或者定时通知类的场景,调用过程是有可能因为网络问题、机器故障等原因导致请求失败,业务场景需要能接受这样的异常场景才能够使用。 2.直连调用 SOFARPC支持指定地址进行调用的场景,设置直连地址即可:

3.泛化调用 SOFARPC泛化调用方式能够在客户端不需要依赖服务端的接口情况下发起调用,目前支持bolt协议。由于不知道服务端的接口,因此需要通过字符串的方式将服务端的接口,调用的方法,参数及结果类进行描述:

如上通过setGeneric设置该服务为泛化服务,设置服务方的接口名。 以GenericService作为泛化服务,通过GenericService能够发起泛化调用。 发起调用时需要传入方法名、方法类型、方法参数。 如果参数或者返回结果在客户端也需要泛化表示则通过GenericObject来实现获取序列化结果等:

(1)接口描述:所有泛化调用都需要在服务引用的时候声明interface为,这是SOFARPC提供的类。 真正的服务接口通过sofa:global-attrs元素的generic-interface属性声明完成接口的描述。

(2)参数描述:由于客户端没有调用服务的参数类,因此通过GenericObject进行描述,GenericObject持有Map<String, Object>类型的变量,能够通过GenericObject提供的对该变量的操作方法将参数类的属性放到Map以此来描述参数类。

(3)发起泛化调用:接口描述通过XML配置声明泛化引用的bean,通过该泛化引用能够发起服务调用,发起泛化调用的第一个参数就是方法名,第二个参数就是参数类的全类名,第三个参数就是描述参数类的 GenericObject。

(4)获取泛化结果:发起泛化调用如果客户端同样没有泛化结果的类,那么同样以GenericObject对调用结果进行描述,通过GenericObject的getField方法能够获取结果类的属性值,通过GenericObject的getType方法能够获取结果类的全类名。

(5)泛化调用示例:SOFARPC泛化调用完整的泛化调用方式:

源码解析 1.调用方式 参考sofa-rpc-boot-projects范例模块( ):

运行调用方式服务端范例类InvokeServerApplication查看调用方式服务端输出日志:

objc对象调用方法详细过程

实例方法存在类对象中 类方法存在元类对象中(元类其实也是一个类对象)

我们先看下类对象的结构布局

我们看到一个类对象就是一个结构体继承与objc_object结构体,我们以前的文章中分析过objc_object结构体,这里简单的说一下objc_object, objc_object里面有一个isa是一个共用体.里面有一个结构体使用位域来存储更多的信息.

superclass是指向父类的指针 cache方法的缓存列表 bits& FAST_DATA_MASK的到class_rw_t结构体

在class_rw_t中 methods就是方法的缓存类表 我们以前讨论分类的的时候也分析过method_array_t methods;结构

methods是一个二维数组, 数组里面的元素是分类的的方法类表 [ [分类方法1a方法,分类方法1b方法], [分类方法2a方法,分类方法2b方法] ] 我们给一个对象发消息的时候就会找到methods这个数组里面然后缓存到cache中,就算给一个对象调用父类的方法也会缓存到cache中的_buckets, 当往_buckets缓存bucket_t的时候_buckets会检查是否需要扩容,需要扩容就会清空所有元素然后,在缓存进来.(这个时候以前的缓存就没有了)

我看到cache结构体中_buckets是一个数组里面是bucket_t结构体_key就是方法名字_buckets就相当于一个离散列表(类似字典)

x0, 寄存器里面存的是receiver消息接受者 //如果receiver为nil跳转到LNilOrTagged // nil check ret (表示return) 如果对象为nil就return

如果对象不为nil就走到CacheLookup 在缓存中寻找

CacheHit 命中缓存 结果是直接调用方法或者返回imp指针

CheckMiss在缓存中没有找到

因为CacheLookup NORMAL传的值是NORMAL 这里我们暂时只分析__objc_msgSend_uncached

我们再分析MethodTableLookup 发现是个宏

bl__class_lookupMethodAndLoadCache3 意思是跳转到__class_lookupMethodAndLoadCache3这个方法.

接下来就来到了lookUpImpOrForward

我们分析一下_class_resolveMethod

如果是实例方法则会调用 _class_resolveInstanceMethod.

我们分析一下_class_resolveInstanceMethod方法SEL_resolveInstanceMethod 是+ (BOOL)resolveInstanceMethod:(SEL)sel 通过objc_msgSend调用这个类方法, 我们可以重写这个类方法, 并且在类方法中 动态添加方法

如果我们动态解析没有做事情 就会来到消息转发_objc_msgForward_impcache这个imp我们发现在汇编中找到,但是经过分析是没有源码的,这里暂不分析汇编 下面列出动态转发的几个方法.

在消息转发阶段如果-forwardingTargetForSelector没有实现,就会调用- methodSignatureForSelector方法自己返回方法签名, 然后调用-forwardInvocation返回一个NSInvocation对象

补充一点如果 消息转发阶段这个消息是类方法就会调用+forwardingTargetForSelector,+ methodSignatureForSelector ,+ forwardInvocation (虽然没有暴露出api)

不管是类方法还是对象方法在消息转发阶段, 其实都是消息接受者调用以上的方法.(这样就可以理解为啥 ,对象方法调用-号类方法调用+号了 因为消息接受者不同)

1消息发送 2动态方法解析 3消息转发

本文原创来源:电气TV网,欢迎收藏本网址,收藏不迷路哦!

相关阅读

添加新评论