美文网首页
Runtime源码解析

Runtime源码解析

作者: lmfei | 来源:发表于2019-10-21 09:54 被阅读0次

Runtime是什么

一套由C、C++、汇编编写的API,为OC提供了运行时的特性

运行时&&编译时

运行时:将一些处理放到运行时去处理,运行时装载到内存

编译时:源代码翻译成机器识别的语言,OC(高级语言)->机器语言->二进制

Runtime的版本 

Legacy、Modern

Runtime提供了源码,通过一些环境配置,可以运行(源码环境配置!)

RuntimeAPI

Runtime常用API(后续补充)

如何探究底层

1.clang

clang的使用:clang -rewrite-objc main.m -o man.cpp

clang的作用:将OC代码转成c++代码

2.通过源码(Open Source)

3.LLDB

4.Debug

Runtime调用方式

1、runtime API

2、NSObject 例:isKindOf

3、OC上层方法 例:selector

Runtime应用

1.黑魔法Method Swizzing

OBJC_EXPORT BOOL

class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,

                const char * _Nullable types) 

参数说明:

cls 被添加方法的类

name 添加的方法的名称的SEL

imp 方法的实现。该函数必须至少要有两个参数,self,_cmd

类型编码

使用method swizzling应该注意的点:

1.+load vs. +initialize

Swizzling应该只在load方法中使用:

oc会在运行时自动调用每个类的两个方法,+load 会在类初始化加载的时候调用;+initialize方法会在程序调用类的第一个实例或者类方法的时候调用。这两个方法都是可选的,只会在实现的时候才去调用。由于method swizzling会影响到全局的状态,因此最小化竞争条件的出现变得很重要,+load方法能够确保在类的初始化时候调用,这能够保证改变应用行为的一致性,而+initialize在执行时并不提供这种保证,实际上,如果没有直接给这个类发送消息,该方法可能都不会调用到。但是在load方法中进行操作,会影响启动速度,需注意!!!

2.dispatch_once 

Swizzling应该只在dispatch_once中完成:

如上,由于swizzling会改变全局状态,所以我们需要在运行时采取一些预防措施。原子性就是其中的一种预防措施,因为它能保证不管有多少个线程,代码只会执行一次。GCD的dispatch_once 能够满足这种需求,因此在method swizzling应该将其作为最佳的实践方式。

3.使用注意

总是调用一个方法的原始实现(除非你有足够好的理由不这么做):API提供了输入和输出的约定,但其中的实现却是黑盒。Swizzling 一个方法但不去调用其原始实现,可能造成私有状态的底层假设被打破,影响程序的其它部分。

2.模型转换

json<->modal

3.分类添加成员变量

objc_setAssociatedObject、objc_getAssociatedObject

相关知识点

对象本质:结构体

方法本质:发送消息

IMP:函数实现指针

对象方法存在类中

类方法存在元类中

类方法以实例的形态存在元类中

对象在类里是一个实例

类在元类中也是一个实例

Runtime源码探索

为什么用汇编写

1、c语言无法通过写一个函数,保留未知参数,跳转到任意的指针;汇编可以,通过寄存器

2、快

objc_msgSend源码探索

objc_msgSend做了什么

objc_msgSend里主要做的事情就是通过sel去查找IMP,查找方式有两种:一种是快速查找方式,通过汇编编写;另一种就是慢速查找,通过C、C++实现。

快速查找流程做的事情有:

1.通过LNilOrTagged判断对象是否为nil,为nil就直接返回,否则继续下一步

2.通过LgetisaDone,处理isa完毕

3.通过CacheLookup NORMAL进行缓存查找,如果查找到直接call IMP,如果没有找到就会执行_obj_msgSend_uncache,该方法就会执行一个__class_lookupMethodAndLoadCache3,该方法往下就会执行慢速查找流程

慢速查找流程做的事情有:

按照isa的走位图,先在自己的类中查找方法,沿着继承链进行方法查找,直到找到NSObject,如果找到则调用并将方法加入缓存,这样下次调用时就会走快速查找流程,;如果还是没有找到就会到元类中去查找,直到找到根元类NSObject

通过以上两个查找流程任未找到方法,就会执行下面两个流程:一个是动态解析;另一个就是方法转发。

动态方法解析:

通过resolveInstanceMethod/resolveClassMethod,进行一次动态添加方法,如果方法被处理了,调用方法;如果未被处理,则进行下一个流程

消息转发过程通过

forwardingTargetForSelect

methodSignatureForSelector

forwardInvocation

三个方法进行处理,如果被处理,则调用方法;如果未处理则调用doesNotRecognizeSelector,然后抛出异常。

相关文章

网友评论

      本文标题:Runtime源码解析

      本文链接:https://www.haomeiwen.com/subject/rsizmctx.html