一、Runtime相关基本概念
runtime 是C、C++、汇编一起写成的一套api,为Objectvie-C提供了运行时机制。
1、运行时
运行时是指一个程序在运行(或者在被执行)的状态。
- 特点:
-
- 装载内存
-
- 运行时功能---依赖于runtime提供的api
2、编译时
将高级语言翻译成机器能够识别的机器语言
- 特点:
-
- 编译时类型检查或静态类型检查
-
- 机器语言---二进制
3、对象本质
- 结构体
- isa
- objc_object、NSObject_IVARS...
- Method、Ivar、Category、objc_property_t
- OC 中实例、类、元类的关系图
4、方法本质
发送消息 objc_msgSend(id self,SEL _cmd)
- 参数一:id self --- 消息接收者
- 参数二:SEL _cmd --- 方法编号,底层是一个字符串,即方法名称
- imp --- 函数实现的指针
- sel--->imp ? 如何通过方法编号找到函数实现??
1.对象方法 --- 存在类中 以实例方法的形态存在
- A、调用本类对象方法 --- objc_msgSend(s, sel_registerName("run"));
LGStudent *s = [LGStudent new];
[s run];
// 方法调用底层编译
// 方法的本质: 消息 : 消息接受者 消息编号 ....参数 (消息体)
objc_msgSend(s, sel_registerName("run"));
- B、调用父类对象方法 --- objc_msgSendSuper(&mySuper,@selector(run));
// 向父类发消息(对象方法)
LGStudent *s = [LGStudent new];
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));
2.类方法 --- 存在元类中 以实例方法的姿态存在
- A、调用本类类方法 --- objc_msgSend(objc_getClass("LGStudent"),sel_registerName("walk"));
// 类方法编译底层
id cls = [LGStudent class];
void *pointA = &cls;
[(__bridge id)pointA walk];
objc_msgSend(objc_getClass("LGStudent"), sel_registerName("walk"));
- B、调用父类类方法 --- objc_msgSendSuper(&myClassSuper,sel_registerName("walk"));
//向父类发消息(类方法)
LGStudent *s = [LGStudent new];
struct objc_super myClassSuper;
myClassSuper.receiver = [s class];
myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));// 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));
5、runtime三种调用方式
- 1、runtime api --- 运行时系统提供的api 接口
- 2、NSObject 提供的Api
- 3、OC 提供的api @selecter ...
二、OC消息机制
1、消息发送---objc_msgSend
OC方法的调用即为消息发送,底层都会走objc_msgSend(id self,SEL _cmd)方法,而objc_msgSend(id self,SEL _cmd)方法实现使用汇编写成,为啥使用汇编而不用C,C++等高级语言?
- 1.汇编写成原因
-
- 寄存器 --- 保留不确定个数的未知参数,还可以跳转到任意的指针, x0~x31
-
- 效率高 比 C/C++ 快0.5-0.8倍
-
- C,C++等高级语言做不到
-
- C等高级语言做不到写一个函数,保留不确定个数的未知参数,还可以跳转到任意的指针;
2、方法查找 --- 发送消息的两种方式,快速查找和慢速查找
消息发送时如何通过方法编号sel找到对应的方法实现imp才是问题的关键,以下是消息机制中查找imp的过程简述:
2.1、快速查找
- 源码中 id objc_msgSend(id self, SEL _cmd, ...)方法, 汇编跟进去到 ENTRY _objc_msgSend,在Apple Github和Apple OpenSource上有源码,但是需要自己编译。
- OC中类的定义objc_class结构体中的 cache_t cache,缓存了方法的sel和imp等数据(sel 与 imp 一一对应生成的哈希表), 快速查找即在此类的缓存中查找;
- ENTRY _objc_msgSend 入口
- 1:LNilOrTagged --- 如果指针小于等于 LNilOrTagged 直接return返回
- 2:LGetIsaDone isa 处理完毕,通过isa 找到相应类class
- 3:CacheLookup NORMAL --- 缓存查找 imp
- 1.CacheHit --- 查找到相应的imp,直接调用即:call imp
- 2.CheckMiss
- 2.1 CheckMiss NORMAL --- __objc_msgSend_uncached(找不到处理,就是说没有相应缓存)
- a.接下来去MethodTableLookup 方法列表查找
- b.__class_lookupMethodAndLoadCache3 --- 此方法为C的方法,汇编查找不到相应imp,从此时开始进入慢速查找
- 2.2 CheckMiss GETIMP --- LGetImpMiss
- 2.3 CheckMiss LOOKUP --- __objc_msgLookup_uncached
- 2.1 CheckMiss NORMAL --- __objc_msgSend_uncached(找不到处理,就是说没有相应缓存)
- 3.add
- 3.1 CacheHit
- 3.2 CheckMiss
- 3.3 JumpMiss
2.2、慢速查找
_class_lookupMethodAndLoadCache3方法跳转到C / C++中,此方法中直接调用lookUpImpOrForward:
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{ // 因为我们查找了没有就没必要传参cache
return lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
- lookUpImpOrForward 部分代码逻辑
- realizeClass(cls)
- _class_initialize (_class_getNonMetaClass(cls, inst))
- retry
- cache_getImp(cls, sel)
- 设计原因:
- 1.并发,走到这时已经有缓存了;
- 2.classRemaped 重映射
- 设计原因:
- 漫长寻找imp的过程,方法查找流程图:
- cache_getImp(cls, sel)
// Try this class's method lists.从自己的方法列表中寻找
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);//如果找到了就去填充到缓存
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.从递归父类去找知道NSObject
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
2.3、动态方法解析
2.3.1 动态方法解析源码分析
快速查找和慢速查找都没有找到,走这里
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);// 动态方法解析主要方法
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
- _class_resolveMethod(cls, sel, inst) --- 动态方法解析调用
/***********************************************************************
* _class_resolveMethod
* Call +resolveClassMethod or +resolveInstanceMethod.
* Returns nothing; any result would be potentially out-of-date already.
* Does not check if the method already exists.
**********************************************************************/
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
- 不是元类时调用 _class_resolveInstanceMethod(cls, sel, inst)
/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);/// 这里系统通过消息发送调用了一次SEL_resolveInstanceMethod
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
- 是元类时调用 _class_resolveClassMethod(cls, sel, inst),当依旧找不到imp时,再调用 _class_resolveInstanceMethod(cls, sel, inst);
/***********************************************************************
* _class_resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(_class_getNonMetaClass(cls, inst), SEL_resolveClassMethod, sel);///这里系统通过消息发送调用了一次SEL_resolveClassMethod
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
2.3.2 动态方法解析实现处理
#pragma mark - 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(run)) {
// 我们动态解析我们的 对象方法
NSLog(@"对象方法解析走这里");
SEL readSEL = @selector(readBook);
Method readM= class_getInstanceMethod(self, readSEL);
IMP readImp = method_getImplementation(readM);
const char *type = method_getTypeEncoding(readM);
return class_addMethod(self, sel, readImp, type);
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(walk)) {
// 我们动态解析我们的 类方法
NSLog(@"类方法解析走这里");
SEL hellowordSEL = @selector(helloWord);
// 类方法就存在我们的元类的方法列表
// 类 类犯法
// 元类 对象实例方法
// Method hellowordM1= class_getClassMethod(self, hellowordSEL);
Method hellowordM= class_getInstanceMethod(object_getClass(self), hellowordSEL);
IMP hellowordImp = method_getImplementation(hellowordM);
const char *type = method_getTypeEncoding(hellowordM);
NSLog(@"%s",type);
return class_addMethod(object_getClass(self), sel, hellowordImp, type);
}
return [super resolveClassMethod:sel];
}
2.4、消息转发
2.4.1 消息转发源码分析
动态方法解析没有实现处理,就会进入消息转发
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
- _objc_msgForward_impcache --- 只有汇编调用 没有源码实现
- 可以通过instrumentObjcMessageSends 方法,打印出方法执行过程中调用的所有OC方法
2.4.2 消息转发实现处理
消息转发流程图
- forwardingTargetForSelector 消息转发到新的target,若未对消息进行处理,则会再次调用
+ (id)forwardingTargetForSelector:(SEL)aSelector{
// 自定义处理 -- crash
// 防止奔溃
// if (aSelector == @selector(walk)) {
// 转发给我们的LGStudent 对象
// return [LGStudent new];
// }
NSLog(@"%s",__func__);
return [super forwardingTargetForSelector:aSelector];
}
- methodSignatureForSelector --- 若forwardingTargetForSelector 没有实现消息转发,就只能方法签名了,方法重新签名可能会改变方法的type,根据需要响应的方法确定type
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
// 处理方法签名
NSLog(@"%s",__func__);
if (aSelector == @selector(walk)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- forwardInvocation 对重新签名(改变type)过的sel,进行消息转发
+ (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
NSString *sto = @"奔跑少年";
anInvocation.target = [LGStudent class];
[anInvocation setArgument:&sto atIndex:2];
NSLog(@"%@",anInvocation.methodSignature);
anInvocation.selector = @selector(run:);
[anInvocation invoke];
}
由于本人水平有限,文中如有不足之处,望大神指出。
如果你看完后觉得对你有所帮助,勿忘点赞+关注。
赠人玫瑰,手有余香。