iOS开发底层之消息的快速与慢速转发 - 11
Posted iOS_developer_zhong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发底层之消息的快速与慢速转发 - 11相关的知识,希望对你有一定的参考价值。
文章目录
一、instrumentObjcMessageSends 系统日志探索
1. 它是什么?
instrumentObjcMessageSends是系统的日志,是苹果的私有API,我们可以控制log开关,打印日志信息。
2. 它的由来?
//在查看 IMP
//1.lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) 方法的时候。 在查找到IMP, done 后续,
//2. 调用了 log_and_fill_cache(cls, imp, sel, inst, curClass);
// 源码如下
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
#if SUPPORT_MESSAGE_LOGGING
if (slowpath(objcMsgLogEnabled && implementer))
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
#endif
cls->cache.insert(sel, imp, receiver);
// 3. 发现了 objcMsgLogEnabled 这个字段,,全局搜索下这个字段。 发现它默认是false,就只有在一个地方进行了设置
//4. 设置的方法为 void instrumentObjcMessageSends(BOOL flag) ,所以我们可以在源码中,先申明这个方法,然后在调用,系统会帮我们找到他的实现。
3. 日志位置
快捷键: command + shift + g
文件位置为: /tmp/msgSend-***
4. 代码使用
//1. 定义下,不然会报找不到这个api错误
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[])
@autoreleasepool
// 2. 打开记录日志操作
instrumentObjcMessageSends(true);
Person *person = [Person alloc];
[person sayHello];
// 3. 关闭日志操作
instrumentObjcMessageSends(false);
NSLog(@"Hello, World!");
return 0;
二、消息转发流程
imp查询流程总结
为了查找到 imp, 总结下一共经过的流程,防止思路跟不上。
0. 寻找 sel对应的 imp
1. objc_msgSend cache 快速查找
2. 慢速方法查找 methlist
3. 动态方法决议 resolveInstanceMethod
4. 消息快速转发(让别人做 ,一般可以定义一个专门的对象,进行消息处理) forwardingTargetForSelector
5. 消息慢速转发 , (比快速转发更加灵活) methodSignatureForSelector 和 forwardInvocation 配对出现。
1.动态方法决议后续转发流程
上面通过系统的日志,故意让方法找不到,让系统崩溃,然后打开日志看到整个的方法转发过程,里面有好几个方法。
+ LGPerson NSObject resolveInstanceMethod:
+ LGPerson NSObject resolveInstanceMethod:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject methodSignatureForSelector:
- LGPerson NSObject methodSignatureForSelector:
+ LGPerson NSObject resolveInstanceMethod:
+ LGPerson NSObject resolveInstanceMethod:
- LGPerson NSObject doesNotRecognizeSelector:
- LGPerson NSObject doesNotRecognizeSelector:
第一个方法resolveInstanceMethod这个是我们上篇博客介绍的,方法的动态决议。
重点看后面的方法。
2. forwardingTargetForSelector 快速转发流程
// 对象方法快速转发
-(id)forwardingTargetForSelector:(SEL)aSelector
NSLog(@"%@ : %s",self,__func__);
// 背锅侠 - 专业处理没有响应的方法
// 方式二: 指定对象,去添加方法并实现。
// IMP myImp = class_getMethodImplementation([LGTeacher class], aSelector);
// Method method = class_getInstanceMethod([LGTeacher class], aSelector);
//
// const char *type = method_getTypeEncoding(method);
//
// bool isSuccess = class_addMethod([LGTeacher class], aSelector, myImp, type);
// 方式一: 知道这个对象里面实现了方法
return [LGTeacher alloc];
3. methodSignatureForSelector慢速转发流程
// 对象方法的慢速转发 相比于快速转发更加灵活,可以选择处理或者不处理。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
NSLog(@"%@ : %s",self,__func__);
if (aSelector == @selector(say666)) // 拦截这个方法。 这里没有实现,只是保存这个事务,
Method method = class_getInstanceMethod([LGTeacher class], aSelector);
const char *type = method_getTypeEncoding(method);
return [NSMethodSignature signatureWithObjCTypes:type];
return [super methodSignatureForSelector:aSelector];
// 和methodSignatureForSelector 成对出现。不然拦截不到错误。
- (void)forwardInvocation:(NSInvocation *)anInvocation
SEL selector = anInvocation.selector ;
LGTeacher *teacher = [[LGTeacher alloc] init];
if ( [self respondsToSelector:selector]) // 自己能处理
[anInvocation invoke];
else if ([teacher respondsToSelector:selector]) // 指定对象处理
[anInvocation invokeWithTarget:teacher];
else // 不想处理 , 可上传日志给服务器,保存记录。
NSLog(@"----%@------%@--", anInvocation.target, NSStringFromSelector(anInvocation.selector));
4.hoper反编译cf
-
lldb命令: bt //打印堆栈信息。 查看下崩溃的堆栈里面有什么信息。
上面的图片,查到崩溃信息,发现最终定位到 CoreFoundation 中的 forwarding,那他最后到底做了什么呢, 只能通过查看CoreFoundation 反编译下,才能揭晓它到底做了什么? -
准备: 需要下载 hopper 软件。 下载hopper
CoreFoundation 动态库。
把 CoreFoundation拖入Hopper 查看下 ,然后搜索
___forwarding___
看到伪代码为:
int ____forwarding___(int arg0, int arg1)
rsi = arg1;
rdi = arg0;
r15 = rdi;
rcx = COND_BYTE_SET(NE);
if (rsi != 0x0)
r12 = *_objc_msgSend_stret;
else
r12 = *_objc_msgSend;
rax = rcx;
rbx = *(r15 + rax * 0x8);
rcx = *(r15 + rax * 0x8 + 0x8);
var_140 = rcx;
r13 = rax * 0x8;
if ((rbx & 0x1) == 0x0) goto loc_649bb;
loc_6498b:
rcx = **_objc_debug_taggedpointer_obfuscator;
rcx = rcx ^ rbx;
rax = rcx >> 0x1 & 0x7;
if (rax == 0x7)
rcx = rcx >> 0x4;
rax = (rcx & 0xff) + 0x8;
if (rax == 0x0) goto loc_64d48;
loc_649bb:
var_148 = r13;
var_138 = r12;
var_158 = rsi;
rax = object_getClass(rbx);
r12 = rax;
r13 = class_getName(rax);
if (class_respondsToSelector(r12, @selector(forwardingTargetForSelector:)) == 0x0) goto loc_64a67;
loc_649fc:
rdi = rbx;
rax = [rdi forwardingTargetForSelector:var_140];
if ((rax == 0x0) || (rax == rbx)) goto loc_64a67;
loc_64a19:
r12 = var_138;
r13 = var_148;
if ((rax & 0x1) == 0x0) goto loc_64a5b;
loc_64a2b:
rdx = **_objc_debug_taggedpointer_obfuscator;
rdx = rdx ^ rax;
rcx = rdx >> 0x1 & 0x7;
if (rcx == 0x7)
rcx = (rdx >> 0x4 & 0xff) + 0x8;
if (rcx == 0x0) goto loc_64d45;
loc_64a5b:
*(r15 + r13) = rax;
r15 = 0x0;
goto loc_64d82;
loc_64d82:
if (**___stack_chk_guard == **___stack_chk_guard)
rax = r15;
else
rax = __stack_chk_fail();
return rax;
loc_64d45:
rbx = rax;
goto loc_64d48;
loc_64d48:
if ((*(int8_t *)__$e48aedf37b9edb179d065231b52a648b & 0x10) != 0x0) goto loc_64ed1;
loc_64d55:
*(r15 + r13) = _getAtomTarget(rbx);
___invoking___(r12, r15);
if (*r15 == rax)
*r15 = rbx;
goto loc_64d82;
loc_64ed1:
____forwarding___.cold.4();
rax = *(rdi + 0x8);
return rax;
loc_64a67:
var_138 = rbx;
if (strncmp(r13, "_NSZombie_", 0xa) == 0x0) goto loc_64dc1;
loc_64a8a:
rax = class_respondsToSelector(r12, @selector(methodSignatureForSelector:));
r14 = var_138;
var_148 = r15;
if (rax == 0x0) goto loc_64dd7;
loc_64ab2:
rax = [r14 methodSignatureForSelector:var_140];
rbx = var_158;
if (rax == 0x0) goto loc_64e3c;
loc_64ad5:
r12 = rax;
rax = [rax _frameDescriptor];
r13 = rax;
if (((*(int16_t *)(*rax + 0x22) & 0xffff) >> 0x6 & 0x1) != rbx)
rax = sel_getName(var_140);
rcx = "";
if ((*(int16_t *)(*r13 + 0x22) & 0xffff & 0x40) == 0x0)
rcx = " not";
r8 = "";
if (rbx == 0x0)
r8 = " not";
_CFLog(0x4, @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'. Signature thinks it does%s return a struct, and compiler thinks it does%s.", rax, rcx, r8, r9, stack[-360]);
rax = object_getClass(r14);
rax = class_respondsToSelector(rax, @selector(_forwardStackInvocation:));
var_150 = r13;
if (rax == 0x0) goto loc_64c19;
loc_64b6c:
if (*0x5c2700 != 0xffffffffffffffff)
dispatch_once(0x5c2700, ^ /* block implemented at ______forwarding____block_invoke */ );
r15 = [NSInvocation requiredStackSizeForSignature:r12];
rsi = *0x5c26f8;
rsp = rsp - ___chkstk_darwin(@class(NSInvocation), rsi, r12, rcx);
r13 = &stack[-360];
__bzero(r13, rsi);
___chkstk_darwin(r13, rsi, r12, rcx);
rax = objc_constructInstance(*0x5c26f0, r13);
var_140 = r15;
[r13 _initWithMethodSignature:r12 frame:var_148 buffer:&stack[-360] size:r15];
[var_138 _forwardStackInvocation:r13];
r14 = 0x1;
goto loc_64c76;
loc_64c76:
if (*(int8_t *)(r13 + 0x34) != 0x0)
rax = *var_150;
if (*(int8_t *)(rax + 0x22) < 0x0)
rcx = *(int32_t *)(rax + 0x1c);
rdx = *(int8_t *)(rax + 0x20) & 0xff;
memmove(*(rdx + var_148 + rcx), *(rdx + rcx + *(r13 + 0x8)), *(int32_t *)(*rax + 0x10));
rax = [r12 methodReturnType];
rbx = rax;
rax = *(int8_t *)rax;
if ((rax != 0x76) && (((rax != 0x56) || (*(int8_t *)(rbx + 0x1) != 0x76))))
r15 = *(r13 + 0x10);
if (r14 != 0x0)
r15 = [[NSData dataWithBytes:r15 length:var_140] bytes];
[r13 release];
rax = *(int8_t *)rbx;
if (rax == 0x44)
asm fld tword [r15] ;
else
r15 = ____forwarding___.placeholder;
if (r14 != 0x0)
r15 = ____forwarding___.placeholder;
[r13 release];
goto loc_64d82;
loc_64c19:
if (class_respondsToSelector(object_getClass(r14), @selector(forwardInvocation:)) == 0x0) goto loc_64ec2;
loc_64c3b:
rax = [NSInvocation _invocationWithMethodSignature:r12 frame:var_148];
r13 = rax;
[r14 forwardInvocation:rax];
var_140 = 0x0;
r14 = 0x0;
goto loc_64c76;
loc_64ec2:
rdi = &var_130;
____forwarding___.cold.3(rdi, r14);
goto loc_64ed1;
loc_64e3c:
rax = sel_getName(var_140);
r14 = rax;
rax = sel_getUid(rax);
if (rax != var_140)
_CFLog(0x4, @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort", var_140, r14, rax, r9, stack[-360]);
if (class_respondsToSelector(object_getClass(var_138), @selector(doesNotRecognizeSelector:)) == 0x0)
____forwarding___.cold.2(var_138);
(*_objc_msgSend)(var_138, @selector(doesNotRecognizeSelector:));
asm ud2 ;
rax = loc_64ec2(rdi, rsi);
return rax;
loc_64dd7:
rbx = class_getSuperclass(r12);
r14 = object_getClassName(r14);
if (rbx == 0x0)
_CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- did you forget to declare the superclass of '%s'?", var_138, r14, object_getClassName(var_138), r9, stack[-360]);
else
_CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- trouble ahead", var_138, r14, r8, r9, stack[-360]);
goto loc_64e3c;
loc_64dc1:
r14 = @selector(forwardingTargetForSelector:);
____forwarding___.cold.1(var_138, r13, var_140, rcx, r8);
goto loc_64dd7;
说明:
是不是太惊喜了。 CoreFoundation底层是按照这个顺序依次进行调用的。
- 源码中依次为快速转发方法 forwardingTargetForSelector:(SEL)aSelector
- 慢速转发方法 methodSignatureForSelector 与for
以上是关于iOS开发底层之消息的快速与慢速转发 - 11的主要内容,如果未能解决你的问题,请参考以下文章
iOS开发底层之方法的慢速查找流程探索+方法动态决议上 - 10
iOS开发底层之方法的慢速查找流程探索+方法动态决议上 - 10