标签:subject serve etc original 参数 nal 就会 get 学习
重温Objective-C的消息机制
消息转发机制:
- 首先在该类的缓存方法列表cache_method_list中查找,是否存在相关方法
- 上一步中若没有命中,则从方法列表 objc_method_list中查找
- 上一步中若没有命中,则从父类super的方法列表 objc_method_list中查找,直至根类NSObject
- 上一步中若没有命中,则进入消息转发流程,一共分为三步:类的动态方法解析、备用接收者对象、完整消息转发
- 动态方法解析:也就是
+(BOOL)resolveClassMethod:
方法或+(BOOL)resolveInstanceMethod:(SEL)sel
方法。该方法允许向当前对象添加方法实现。
1 2 3 4 5 6 7 8 9
| +(BOOL)resolveInstanceMethod:(SEL)aSEL { if(aSEL == @selector(doFoo:)) { class_addMethod([self class],aSEL,(IMP)fooMethod,"v@:"); return YES; } return [super resolveInstanceMethod]; }
|
- 备用接收者对象:
– (id)forwardingTargetForSelector:(SEL)aSelector
方法,
该方法提供一次机会引导Objective-C RunTime 到备用接收者对象上。
1 2 3 4 5 6 7
| -(id)forwardingTargetForSelector:(SEL)aSelector{ if(aSelector == @selector(doFoo:)){ return alternateObject; } return [super forwardingTargetForSelector:aSelector];
}
|
- 完整消息转发:
– (void)forwardInvocation:(NSInvocation *)anInvocation
方法,NSInvocation
是Objective-C 消息的对象形式,它包含了消息的所有信息,这也就意味着,一旦有了NSInvocation
对象,你就可以改变这个消息的所有信息,包括目标对象、selector以及参数。例如可以这样做(当然RAC与Aspect所做的事情远远不止这么简单啦):
1 2 3 4 5 6 7 8 9
| -(void)forwardInvocation:(NSInvocation *)invocation { SEL invSEL = invocation.selector; if([altObject respondsToSelector:invSEL]) { [invocation invokeWithTarget:altObject]; } else { [self doesNotRecognizeSelector:invSEL]; } }
|
rac_signalForSelector 源码走读
- (RACSignal *)rac_signalForSelector:(SEL)selector
方法位于NSObject+RACSelectorSignal 这个category下。先来看一下.h 头文件。头文件只对外暴露了以下两个方法。
1 2 3 4
| - (RACSignal *)rac_signalForSelector:(SEL)selector;
- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol;
|
再来看一下.m 文件
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (RACSignal *)rac_signalForSelector:(SEL)selector { NSCParameterAssert(selector != NULL);
return NSObjectRACSignalForSelector(self, selector, NULL); }
- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol { NSCParameterAssert(selector != NULL); NSCParameterAssert(protocol != NULL);
return NSObjectRACSignalForSelector(self, selector, protocol); }
|
这两方法最终都调用了C函数NSObjectRACSignalForSelector
。
- 获取Selector的别名
aliasSelector
- 是否存在关联对象,有则跳至步骤8
- 替换类
RACSwizzleClass(self)
- 获取替换的类,这一步主要是替换了原类中
forwardInvocation:
的实现。
- 创建RACSubject对象,并设置关联对象
- 获取原方法
class_getInstanceMethod(class, selector);
- 若原方法不存在,则向该类添加方法
class_addMethod(class, selector, _obj 大专栏 RAC rac_signalForSelector 如何实现对象方法的hookc_msgForward, typeEncoding)
。值得注意的是,方法体为_objc_msgForward
,即上一节中提到的完整消息转发方法的方法体。
- 若原方法存在,则向该类添加
aliasSelector
,其实现即为原方法的实现,并将原方法的实现替换为_objc_msgForward
- 返回RACSubject对象
到此为止,rac_signalForSelector 的全部工作便是将目标selector
的实现替换成了消息转发。
接下来,看看消息转发的实现部分,也就是步骤2中的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static void RACSwizzleForwardInvocation(Class class) { SEL forwardInvocationSEL = @selector(forwardInvocation:); Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);
// Preserve any existing implementation of -forwardInvocation:. void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL; if (forwardInvocationMethod != NULL) { originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod); }
id newForwardInvocation = ^(id self, NSInvocation *invocation) { BOOL matched = RACForwardInvocation(self, invocation); if (matched) return;
if (originalForwardInvocation == NULL) { [self doesNotRecognizeSelector:invocation.selector]; } else { originalForwardInvocation(self, forwardInvocationSEL, invocation); } };
class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@"); }
|
源码很简单,就是hook了forwardInvocation:
方法,当触发完整消息转发时,首先交由RACForwardInvocation
响应,若RACForwardInvocation
响应了则结束消息转发,否则走原消息转发流程。
接下来看看RACForwardInvocation
的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { SEL aliasSelector = RACAliasForSelector(invocation.selector); RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
Class class = object_getClass(invocation.target); BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; if (respondsToAlias) { invocation.selector = aliasSelector; [invocation invoke]; }
if (subject == nil) return respondsToAlias;
[subject sendNext:invocation.rac_argumentsTuple]; return YES; }
|
- 获取
selector
的别名 aliasSelector
- 获取关联对象
subject
- 执行
aliasSelector
,并通过subject
将返回值以RACTuple的形式发送出去。
总结一下RAC实现原理:RAC利用RunTime机制将所要监听的方法,全部转发到forwardInvocation:
,并像class
添加了别名方法aliasSelector
,其方法体即原方法的方法体。那么当外部调用原方法时,就会触发消息转发流程。而RAC拦截了forwardInvocation:
,并执行别名方法aliasSelector
,最后将返回结果发送出去。
RAC在实现过程中,对Runtime的使用相当的深入。针对各种情况的考虑也是相当的周全,其实现也相当严谨,特别值得学习。
深度改造Runtime的弊端
RAC 通过深度改造对象的消息机制以达到AOP的目的,对于日常开发来说相当便利。不过值得注意的是:当一个项目内存在多个库深度改造对象的消息机制,就会产生不可避免的冲突,比如ASpect
这个库,它的实现原理有RAC完全一样,应该都是借鉴了KVO的实现方式,唯一的不同点在于ASpect
的消息forwardInvocation:
实现比RAC稍微多了一步:当对象无法响应selector时,会调用 doesNotRecognizeSelector:
抛出异常。
若同时使用这两个库对同一对象的同一方法Hook,那么该方法将无法被执行,并存在Crash隐患。
经过以上的研究,对Runtime有了更深入的了解。
RAC rac_signalForSelector 如何实现对象方法的hook
标签:subject serve etc original 参数 nal 就会 get 学习
原文地址:https://www.cnblogs.com/lijianming180/p/12389466.html