AI智能
改变未来

iOS OC 方法查找流程

iOS OC 方法查找流程

  • 前言
  • 1. `_class_lookupMethodAndLoadCache3`方法查找流程
  • 2. 面试题

前言

上一篇关于

方法的本质

的探索中,我们知道了

方法

的底层是调用

objc_msgSend

发送消息,并对

objc_msgSend

的底层汇编进行了分析。当用

汇编

快速查找,未查找到

方法缓存

时,会调用

MethodTableLookup

,然后调用

_class_lookupMethodAndLoadCache3

,从

汇编

转到

C

,开启一系列的慢速查找,接下来我们对

_class_lookupMethodAndLoadCache3

的方法查找流程进行分析。

1.

_class_lookupMethodAndLoadCache3

方法查找流程

假如当调用

LGStudent

调用对象方法

sayHello

时,底层通过

objc_msgSend

发送消息,通过汇编在

LGStudent

的 cache中快速查找

sayHello

的缓存,未找到时,会来的

_class_lookupMethodAndLoadCache3

,方法如下:

IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls){return lookUpImpOrForward(cls, sel, obj,YES/*initialize*/, NO/*cache*/, YES/*resolver*/);// NO/*cache*/ 没有方法缓存}

IMP lookUpImpOrForward()

方法中

  1. 先根据传入的参数
    cache

    ,为

    ture

    时,再次通过

    cache_getImp(cls, sel)

    方法,用汇编去查找

    imp

    ,查找到直接返回

    imp

    ;为

    false

    时,直接跳过。

  2. 判断
    !cls->isRealized()

    ,调用

    realizeClass(cls)

    ,做准备工作(根据

    class

    rw data()->flags & RW_REALIZED

    ),比如

    父类 元类 rw ro

    等。

if (!cls->isRealized()) {realizeClass(cls);}
  1. 准备工作完成后,再次尝试
    cache_getImp(cls, sel)

    ,查找到

    imp

    ,直接返回

    imp

    .

imp = cache_getImp(cls, sel);if (imp) goto done;
  1. 试图在

    class\'s method lists

    中查找

    方法

    ,通过

    getMethodNoSuper_nolock

    获取

    meth

    4.1.

    getMethodNoSuper_nolock

    中:

    循环取出

    mlists

    后,调用

    method_t *m = search_method_list(*mlists, sel)

    ,通过

    sel

    去匹配,匹配到直接返回。

    4.2.

    方法

    找到后,调用

    log_and_fill_cache

    ,然后调用

    cache_fill (cls, sel, imp, receiver)

    ,调用

    cache_fill_nolock(cls, sel, imp, receiver)

    ,进入

    方法

    缓存流程,判断是否有缓存,是否超出容量的3/4,是否需要扩容,然后找到

    bucket

    , 偏移

    _occupied

    ,然后

    set(key, imp)

    ,将方法缓存到

    Class

    cache_t cache

    中,方便下次调用时,快速查找。

    Method meth = getMethodNoSuper_nolock(cls, sel);if (meth) {log_and_fill_cache(cls, meth->imp, sel, inst, cls);imp = meth->imp;goto done;}
  2. class\'s method lists

    中未找到方法时,即:

    sayHello

    方法在

    LGStudent

    中未定义,那么会试图在父类的缓存和方法列表中

    superclass caches and method lists

    查找。

    在查找

    superclass

    过程中,按照

    父类

    ->

    元类

    ->

    根元类

    ->

    根类(NSObject)

    的顺序,依次循环查找,先通过汇编

    cache_getImp

    查找

    superclass

    cache

    ,缓存命中,调用

    log_and_fill_cache

    ,进入

    方法

    缓存流程直接返回

    imp

    ; 在缓存中未找到时,查找

    Superclass method list

    ,流程同 4.1、4.2 步骤,源码如下:

// Try superclass caches and method lists.{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) {// 进入方法缓存流程log_and_fill_cache(cls, imp, sel, inst, curClass);goto done;}else {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;}}}
  1. class\'s method lists

    superclass caches and method lists

    中都没有找到调用的方法时,即:调用的

    方法

    类 父类 元类

    中都未实现,那么调用

    _class_resolveMethod(cls, sel, inst)

    方法,进行方法动态解析。

// 源码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;}
  1. _class_resolveMethod

    方法实现

void _class_resolveMethod(Class cls, SEL sel, id inst){if (! cls->isMetaClass()) {  // 判断是否是元类// try [cls resolveInstanceMethod:sel]// 类 此时类中已经没有方法 直接执行  _class_resolveInstanceMethod// 执行 + (BOOL)resolveInstanceMethod:(SEL)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

方法实现:

static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst){// 查找_resolveInstanceMethod方法,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);// Cache the result (good or bad) so the resolver doesn\'t fire next time.// +resolveInstanceMethod adds to self a.k.a. clsIMP imp = lookUpImpOrNil(cls, sel, inst,NO/*initialize*/, YES/*cache*/, NO/*resolver*/);}

_class_resolveInstanceMethod

方法中,
先通过上面的方法查找流程查找

resolveInstanceMethod

方法的

IMP

(该方法是系统

NSObject

默认实现,

+ (BOOL)resolveInstanceMethod:(SEL)sel

,默认返回

NO

),查找到

IMP

后,向

cls

发送

resolveInstanceMethod

消息,参数是

sel

(为实现的方法)。

所以,我们可以在重新系统的 resolveInstanceMethod 方法,在此方法中对为实现的方法进行动态解析,防止因为调用未实现的方法引起的系统崩溃。

假如,我们在

resolveInstanceMethod

方法中,对方法进行了动态解析,那么这个方法的

IMP

,会加入到对应的

cache

中,然后跳转到 步骤6,然后

retry

,重新查找。

  1. retry

    之后,依然没有查找到

    IMP

    ,调用下面

    汇编


    __objc_msgForward_impcache

    的汇编代码:

    然后调用

    __objc_forward_handler

    ,从

    汇编

    调用

    OC

    方法,如下:


到此,报出经典错误。

补充:重新

resolveInstanceMethod

示例:

+ (BOOL)resolveInstanceMethod:(SEL)sel{// saySomething为为实现方法,当调用次方法时,就调用已经实现的sayHello方法if (sel == @selector(saySomething)) {IMP sayHIMP = class_getMethodImplementation(self, @selector(sayHello));Method sayHMethod = class_getInstanceMethod(self, @selector(sayHello));const char *sayHType = method_getTypeEncoding(sayHMethod);return class_addMethod(self, sel, sayHIMP, sayHType);}NSLog(@\"来了  老弟 - %p\",sel);return [super resolveInstanceMethod:sel];}

2. 面试题

定义下面代码:

LGStudent

调用

对象方法 sayMaster

,是否会崩溃?为什么?

@interface LGPerson : NSObject- (void)sayNB;+ (void)sayHappay;@end#import \"LGPerson.h\"@implementation LGPerson- (void)sayNB{NSLog(@\"%s\",__func__);}+ (void)sayHappay{NSLog(@\"%s\",__func__);}@end//#import \"LGPerson.h\"@interface LGStudent : LGPerson- (void)sayHello;+ (void)sayObjc;@end#import \"LGStudent.h\"@implementation LGStudent- (void)sayHello{NSLog(@\"%s\",__func__);}+ (void)sayObjc{NSLog(@\"%s\",__func__);}@end@interface NSObject (LG)- (void)sayMaster;+ (void)sayEasy;@end@implementation NSObject (LG)- (void)sayMaster{NSLog(@\"%s\",__func__);}+ (void)sayEasy{NSLog(@\"%s\",__func__);}[LGStudent sayMaster];@end

答:不会崩溃,

LGStudent

继承自

LGPerson

LGStudent

调用

sayMaster

方法,因为本身没有

sayMaster

方法,会去父类

LGPerson

中寻找,

LGPerson

同样没有

sayMaster

方法,接下来寻找

LGPerson

的元类,直到寻到 根元类,而 根元类 也没有

sayMaster

方法,最后寻找

根元

类 的

父类NSObject

,

父类NSObject

中有

对象方法 sayMaster

,所以不会崩溃,并且会调用该方法。

  • 点赞1
  • 收藏
  • 分享
  • 文章举报

亮亮不想说话发布了18 篇原创文章 · 获赞 10 · 访问量 265私信关注

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » iOS OC 方法查找流程