IOS开发入门之Objective-C runtime源码学习之IMP寻址(不包括消息转发部分)
白羽 2018-11-23 来源 :网络 阅读 900 评论 0

摘要:本文将带你了解IOS开发入门Objective-C runtime源码学习之IMP寻址(不包括消息转发部分),希望本文对大家学IOS有所帮助。

    本文将带你了解IOS开发入门Objective-C runtime源码学习之IMP寻址(不包括消息转发部分),希望本文对大家学IOS有所帮助。



        

写在前面

这是在看《招聘一个靠谱的iOS》时回答第22题时总结的一篇博客,不过这篇博客中并没有牵涉到底层的代码,而且也留下了几个没有解决的问题,这篇博客将深入runtime源码继续探索这个问题,并尝试解决上篇博客中未解决的问题,本人第一次阅读源码,如果有分析错误的地方,欢迎大家纠正。

引入

首先大家都知道,在oc中调用方法(或者说发送一个消息是)runtime底层都会翻译成objc_msgSend(id self, SEL op, ...),苹果为了优化性能,这个方法是用汇编写成的


   

/********************************************************************

 *

 * id objc_msgSend(id self, SEL _cmd,...);

 *

 ********************************************************************/

 

    ENTRY objc_msgSend

# check whether receiver is nil

teq     a1, #0

    beq     LMsgSendNilReceiver

 

# save registers and load receiver's class for CacheLookup

stmfd   sp!, {a4,v1}

ldr     v1, [a1, #ISA]

 

# receiver is non-nil: search the cache

CacheLookup a2, v1, LMsgSendCacheMiss

 

# cache hit (imp in ip) and CacheLookup returns with nonstret (eq) set, restore registers and call

ldmfd   sp!, {a4,v1}

bx      ip

 

# cache miss: go search the method lists

LMsgSendCacheMiss:

ldmfd sp!, {a4,v1}

b _objc_msgSend_uncached

 

LMsgSendNilReceiver:

    mov     a2, #0

    bx      lr

 

LMsgSendExit:

END_ENTRY objc_msgSend

   

实话说我没有学过汇编,所以看到这段代码我的内心是崩溃的,更可怕的是针对不同的平台,还有不同汇编代码的实现

虽然不懂汇编,但是苹果的注释很详细,看注释也可以大致明白在干什么,首先检查传入的self是否为空,然后根据selector寻找方法实现IMP,找到则调用并返回,否则抛出异常。由此可以有以下伪代码


   

id objc_msgSend(id self, SEL _cmd, ...) {

  Class class = object_getClass(self);

  IMP imp = class_getMethodImplementation(class, _cmd);

  return imp ? imp(self, _cmd, ...) : 0;

}

   

伪代码中我们看到class_getMethodImplementation(Class cls, SEL sel) 方法用来寻找IMP地址,有趣的是苹果真的提供了这个方法,可以让我们调用,通过selector去寻找方法实现IMP,而这个函数的实现,以及其延伸就是这篇博客所要探讨的重点。

正文

在我前面的文章中也说到IMP寻址总共有两种方法:


   

IMP class_getMethodImplementation(Class cls, SEL name);

IMP method_getImplementation(Method m);

   

而在NSObject中提供了几个对class_getMethodImplementation封装的方法


   

+ (IMP)instanceMethodForSelector:(SEL)sel {

    if (!sel) [self doesNotRecognizeSelector:sel];

    return class_getMethodImplementation(self, sel);

}

 

+ (IMP)methodForSelector:(SEL)sel {

    if (!sel) [self doesNotRecognizeSelector:sel];

    return object_getMethodImplementation((id)self, sel);

}

 

- (IMP)methodForSelector:(SEL)sel {

    if (!sel) [self doesNotRecognizeSelector:sel];

    return object_getMethodImplementation(self, sel);

}

   

但这些方法却并没有在头文件中暴露,所以我并不明白苹果这样做的用意,如果有人知道,希望能够告知,感激不尽!
这里出现的object_getMethodImplementation其实就是对class_getMethodImplementation的封装,苹果的解释是:

 

Equivalent to: class_getMethodImplementation(object_getClass(obj), name);

下面我们就暂时把目光转向class_getMethodImplementation这个函数,看看它底层到底是如何实现的



   

IMP class_getMethodImplementation(Class cls, SEL sel)

{

    IMP imp;

 

    if (!cls  ||  !sel) return nil;

 

    imp = lookUpImpOrNil(cls, sel, nil, 

                         YES/*initialize*/, YES/*cache*/, YES/*resolver*/);

 

    // Translate forwarding function to C-callable external version

    if (!imp) {

        return _objc_msgForward;

    }

 

    return imp;

}

   

首先判断传入的参数是否为空,然后进入lookUpImpOrNil这个方法,实际上这个这个方法是对lookUpImpOrForward的简单封装:


   

/***********************************************************************

* lookUpImpOrNil.

* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache

**********************************************************************/

IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 

                   bool initialize, bool cache, bool resolver)

{

    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);

    if (imp == _objc_msgForward_impcache) return nil;

    else return imp;

}

   

注释写的也很清楚,这个方法不会进行消息的转发,而直接返回nil,这个倒是比较有趣,明明调用lookUpImpOrForward可以直接进行消息转发,可是这里偏不这样做,调用消息转发返回nil的函数,然后判断imp为nil时,自己手动返回_objc_msgForward,进行消息转发,还真是有意思,不过苹果在这里做了注释:Translate forwarding function to C-callable external version,将这个转发函数转换为C语言能够调用的版本。
接下来我们继续深入,看一下lookUpImpOrForward是如何实现的:


   

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 

                       bool initialize, bool cache, bool resolver)

{

    Class curClass;

    IMP methodPC = nil;

    Method meth;

    bool triedResolver = NO;

 

    methodListLock.assertUnlocked();

 

    if (cache) {

        methodPC = _cache_getImp(cls, sel);

        if (methodPC) return methodPC;    

    }

 

    if (cls == _class_getFreedObjectClass())

        return (IMP) _freedHandler;

    }

 

 retry:

    methodListLock.lock();

 

    // Ignore GC selectors

    if (ignoreSelector(sel)) {

        methodPC = _cache_addIgnoredEntry(cls, sel);

        goto done;

    }

 

    // Try this class's cache.

    methodPC = _cache_getImp(cls, sel);

    if (methodPC) goto done;

 

    // Try this class's method lists.

    meth = _class_getMethodNoSuper_nolock(cls, sel);

    if (meth) {

        log_and_fill_cache(cls, cls, meth, sel);

        methodPC = method_getImplementation(meth);

        goto done;

    }

 

    // Try superclass caches and method lists.

    curClass = cls;

    while ((curClass = curClass->superclass)) {

        // Superclass cache.

        meth = _cache_getMethod(curClass, sel, _objc_msgForward_impcache);

        if (meth) {

            if (meth != (Method)1) {

                // Found the method in a superclass. Cache it in this class.

                log_and_fill_cache(cls, curClass, meth, sel);

                methodPC = method_getImplementation(meth);

                goto done;

            }

            else {

                // Found a forward:: entry in a superclass.

          &nbsp    

   

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程