不断更新,最新内容查看pdf版本,最新:2018-02-23
第一部分:从方法调用得出的结论
1.1 文字的描述:先来看一段代码:
- ClangClass类有个handle方法
- handle方法里面是创建AClass对象并调用addO方法
- 我们来看看调用addO方法的具体过程
1.2 源代码的描述:
#import "ClangClass.h"
#import "AClass.h"
@interface ClangClass()
@property(nonatomic,strong)AClass *acClass;
@end
@implementation ClangClass
- (instancetype)init{
if (self = [super init]) {
_acClass = [[AClass alloc] init];
}
return self;
}
- (void)handle{
[_acClass addO];
}
@end
1.3 将代码操作一下:
- 进入到上面ClangClass.m的文件夹下面,使用命令编译,命令为:clang -rewrite-objc ClangClass.m
- 看文件ClangClass.cpp得到的结果为,我们具体看调用的代码是:[_acClass addO];
- {是不是感觉一大堆很乱,我也觉得}
static void _I_ClangClass_handle(ClangClass * self, SEL _cmd) {
((void (*)(id, SEL))(void *)objc_msgSend)((id)(*(AClass **)((char *)self + OBJC_IVAR_$_ClangClass$_acClass)), sel_registerName("addO"));
}
// 把上面代码优化下,去掉强制转换的符号得到
objc_msgSend(_acClass, sel_registerName("addO"));
---------------------------------------------
IMP本质:是一个函数指针,保存了方法地址,指向方法的实现,这是由编译器生成的。
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id
---------------------------------------------
得出结论:
1. 调用方法addO就是调用objc_msgSend进行消息传递;
2. 而objc_msgSend就是去找到IMP,来执行实现的代码;
第二部分:消息传递objc_msgSend、objc_msgSendSuper、objc_msgSend_stret、objc_msgSendSuper_stret
objc_msgSend定义:id objc_msgSend(id self, SEL op, …);
self:A pointer that points to the instance of the class that is to receive the message.{消息的接收者}
op:The selector of the method that handles the message.{是selector}
…:A variable argument list containing the arguments to the method.{是参数列表}
id的本质:id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员,根据isa指针就可以顺藤摸瓜找到对象所属的类
typedef struct objc_object *id;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
}
SEL的本质:SEL 是系统在编译过程中,根据 方法的名字 以及 参数序列 生成一个用来区分这个方法的唯一 ID 编号;这个 ID 就是 SEL 类型的。和C的函数指针还不一样,函数指针直接保存了方法的地址,但是SEL只是方法编号。
Selector(typedef struct objc_selector *SEL)
2.1 文字描述objc_msgSend的工作流程:
- 在self中沿着isa找到AClass的类对象;
- 优先在AClass的cache查找addO方法,如果有就返回imp;
- 如果第一次没找到缓存中的addO方法,就查看这个类有没有被释放,如果释放了,就返回;
- 如果这个类没有被释放,就检查这个类有没有实现 +initialize方法,如果有但是没有初始化,就初始化;
- 判断初始化后,尝试再在AClass的cache查找addO方法,如果找不到,就到AClass的methodLists查找addO方法;
- 如果没有在AClass找到addO方法,就会到super_class就是NSObject里面的cache查找;如果在父类的cache找不到,就到父类的methodLists中查找addO方法;
- 如果在父类的methodLists也没有找到addO方法,且如果强制传入resolver为YES,且找不到imp,尝试一次_class_resolveMethod,不尝试消息转发了。
- 如果resolver设置为NO,且还没有找到IMP,就返回_objc_msgForward_impcache开始消息转发;
- 不管上面的哪一步找到imp,都直接返回imp。
2.2 相关:
- _objc_msgForward是用于消息转发的。这个函数的实现并没有在objc-runtime的开源代码里面,而是在Foundation框架里面实现的。加上断点启动程序后,会发现__CFInitialize这个方法会调用objc_setForwardHandler函数来注册一个实现。
- 当向一般对象发送消息时,调用objc_msgSend;当向super发送消息时,调用的是objc_msgSendSuper; 如果返回值是一个结构体,则会调用objc_msgSend_stret或objc_msgSendSuper_stret。
2.3 本质:
- objc_msgSend的本质就是找到imp指针执行代码,而静态语言的imp函数指针在编译的时候就已经确定好了,动态语言是编译后可以变换的。
2.4 源代码描述objc_msgSend的工作流程:
{其实我没有找到这段的实现,就用大神的代码了}
id objc_msgSend(id self, SEL op, ...) {
if (!self) return nil;
IMP imp = class_getMethodImplementation(self->isa, SEL op);
imp(self, op, ...); //调用这个函数,伪代码...
}
// 源码
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) {
// 如果找不到imp就返回消息转发
return _objc_msgForward;
}
return imp;
}
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
// 查找imp或决定转发
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
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();
// Optimistic cache lookup
if (cache) {
// 查找imp,如果有缓存,就返回缓存
methodPC = _cache_getImp(cls, sel);
if (methodPC) return methodPC;
}
// Check for freed class
// 查看释放的类
if (cls == _class_getFreedObjectClass())
// 如果类已经释放,返回_freedHandler
return (IMP) _freedHandler;
// Check for +initialize
// 检查有没有实现+initialize方法
if (initialize && !cls->isInitialized()) {
_class_initialize (_class_getNonMetaClass(cls, inst));
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
// The lock is held to make method-lookup + cache-fill atomic
// with respect to method addition. Otherwise, a category could
// be added but ignored indefinitely because the cache was re-filled
// with the old value after the cache flush on behalf of the category.
retry:
// 加锁
methodListLock.lock();
// 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.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
// 查找父类的方法列表
meth = _class_getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, curClass, meth, sel);
methodPC = method_getImplementation(meth);
goto done;
}
}
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
// 解锁
methodListLock.unlock();
// 如果强制传入resolver为YES,且找不到imp,尝试一次_class_resolveMethod,不尝试消息转发了
_class_resolveMethod(cls, sel, inst);
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
// 方法找不到:尝试一次resolver方法无效;开始转发
_cache_addForwardEntry(cls, sel);
// 返回_objc_msgForward_impcache表示转发
methodPC = _objc_msgForward_impcache;
done:
// 解锁
methodListLock.unlock();
return methodPC;
}
第三部分:消息转发_objc_msgForward
// add1方法是不存在的,现在试试转发
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
AClass *acClass = [[AClass alloc] init];
[acClass performSelector:@selector(add1)];
}
3.1 oc转发文字描述:
- 调用resolveInstanceMethod:方法。允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回YES,重新开始objc_msgSend流程。这次对象会响应这个选择器,一般是因为它已经调用过了class_addMethod。如果仍没有实现,继续下面的动作。
- 如果调用resolveInstanceMethod没有实现,就开始调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非nil对象。否则返回nil,继续下面的动作。注意这里不要返回self,否则会形成死循环。
- 如果调用forwardingTargetForSelector没有效果,就开始调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil;传给一个NSInvocation并传给forwardInvocation:。
- 将第三步获取到的方法签名包装成Invocation传入,调用forwardInvocation:方法,如何处理就在这里面了,并返回非nil。
- 调用doesNotRecognizeSelector:,默认的实现是抛出异常。如果第三步没能获得一个方法签名,执行该步骤 。
3.2 oc转发源代码描述:
使用instrumentObjcMessageSends的方法来查看app的log信息,具体使用方法:在appdelegate.m里面定义如下:
#import "AppDelegate.h"
#import <objc/runtime.h>
void instrumentObjcMessageSends();
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
instrumentObjcMessageSends(YES);
return YES;
}
// 下面是消息转发的log信息
+ AClass NSObject initialize
+ AClass NSObject alloc
- AClass AClass init
- NSObject NSObject init
- AClass NSObject performSelector:
+ AClass NSObject resolveInstanceMethod:
+ AClass NSObject resolveInstanceMethod:
- AClass NSObject forwardingTargetForSelector:
- AClass NSObject forwardingTargetForSelector:
- AClass NSObject methodSignatureForSelector:
- AClass NSObject methodSignatureForSelector:
- AClass NSObject class
- AClass NSObject doesNotRecognizeSelector:
- AClass NSObject doesNotRecognizeSelector:
- AClass NSObject class
第四部分 respondsToSelector、instancesRespondToSelector
// add1方法是不存在的,现在试试转发,看第四部分
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
AClass *acClass = [[AClass alloc] init];
[acClass respondsToSelector:@selector(add1)];
}
4.1 文字分析:
- 如果没有找到imp方法,就尝试一次resolveInstanceMethod,不会进行消息转发
- 不会进行消息转发,所以就算找不到方法,也不会报doesNotRecognizeSelector这个找不到方法的报错了
- 主要的原因,看respondsToSelector的源码就知道,在下面
4.2 源码分析:
+ (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector_inst(object_getClass(self), sel, self);
}
- (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector_inst([self class], sel, self);
}
---------------------------------------------
+ (BOOL)instancesRespondToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector(self, sel);
}
---------------------------------------------
BOOL class_respondsToSelector(Class cls, SEL sel)
{
return class_respondsToSelector_inst(cls, sel, nil);
}
bool class_respondsToSelector_inst(Class cls, SEL sel, id inst)
{
IMP imp;
if (!sel || !cls) return NO;
// Avoids +initialize because it historically did so.
// We're not returning a callable IMP anyway.
// 这里的resolver传入的是YES,所以当找不到方法的时候,就只会执行一次resolveInstanceMethod方法,避开了消息转发,具体看最上面的lookUpImpOrNil方法
imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, YES/*resolver*/);
return bool(imp);
}
---------------------------------------------
// respondsToSelector消息转发部分的log
+ AClass NSObject initialize
+ AClass NSObject alloc
- AClass AClass init
- NSObject NSObject init
- AClass NSObject respondsToSelector:
- AClass NSObject class
+ AClass NSObject resolveInstanceMethod:
+ AClass NSObject resolveInstanceMethod:
- AClass NSObject dealloc
第五部分 methodForSelector、instanceMethodForSelector
// add1方法是不存在的,现在试试转发,看第四部分
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
AClass *acClass = [[AClass alloc] init];
[acClass methodForSelector:@selector(add1)];
}
---------------------------------------------
+ (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);
}
---------------------------------------------
+ (IMP)instanceMethodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return class_getMethodImplementation(self, sel);
}
5.1 看源码可以知道,是和respondsToSelector一样的实现机制:没有消息转发
+ AClass NSObject initialize
+ AClass NSObject alloc
- AClass AClass init
- NSObject NSObject init
- AClass NSObject methodForSelector:
+ AClass NSObject resolveInstanceMethod:
+ AClass NSObject resolveInstanceMethod:
- AClass NSObject dealloc