摘要:本文将带你了解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.
 
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号