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,然后抛出异常。
网友评论