上篇文章简单分析了修复部分的代码实现,本文直接开始由调用过程入手。
调用过程 1.1 JPForwardInvocation 1.1.2 入口 还是以上篇文章的handleBtn
方法作为例子阐述整个的调用过程。
当点击模拟器的Push JPTableViewController
按钮时,handleBtn
的方法被调用,由上篇文章4.3.2中以下代码我们已经知道selector
的实现实际走消息转发的流程。
1 2 IMP msgForwardIMP = _objc_msgForward; class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
同样4.3.2中的以下代码我们知道消息转发的实现已经替换为静态方法JPForwardInvocation
的具体实现,因此下面我们具体看看这里的实现。
1 2 3 4 if (class_getMethodImplementation(cls, @selector (forwardInvocation:)) != (IMP)JPForwardInvocation) { IMP originalForwardImp = class_replaceMethod(cls, @selector (forwardInvocation:), (IMP)JPForwardInvocation, "v@:@" ); class_addMethod(cls, @selector (ORIGforwardInvocation:), originalForwardImp, "v@:@" ); }
1.1.1 实现 代码片段一:
1 2 3 4 5 6 7 8 9 10 11 12 id slf = assignSlf;NSMethodSignature *methodSignature = [invocation methodSignature];NSInteger numberOfArguments = [methodSignature numberOfArguments]; NSString *selectorName = NSStringFromSelector (invocation.selector );NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@" , selectorName];SEL JPSelector = NSSelectorFromString (JPSelectorName); if (!class_respondsToSelector(object_getClass(slf), JPSelector)) { JPExcuteORIGForwardInvocation(slf, selector, invocation); return ; }
判断新的selector是否在该类中已经实现,否则就走原始方法的消息转发的流程。
代码片段二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 NSMutableArray *argList = [[NSMutableArray alloc] init];if ([slf class] == slf) { [argList addObject:[JSValue valueWithObject:@{@"__clsName" : NSStringFromClass ([slf class])} inContext:_context]]; } else if ([selectorName isEqualToString:@"dealloc" ]) { [argList addObject:[JPBoxing boxAssignObj:slf]]; deallocFlag = YES ; } else { [argList addObject:[JPBoxing boxWeakObj:slf]]; } ... if (_currInvokeSuperClsName) { Class cls = NSClassFromString (_currInvokeSuperClsName); NSString *tmpSelectorName = [[selectorName stringByReplacingOccurrencesOfString:@"_JPSUPER_" withString:@"_JP" ] stringByReplacingOccurrencesOfString:@"SUPER_" withString:@"_JP" ]; if (!_JSOverideMethods[cls][tmpSelectorName]) { NSString *ORIGSelectorName = [selectorName stringByReplacingOccurrencesOfString:@"SUPER_" withString:@"ORIG" ]; [argList removeObjectAtIndex:0 ]; id retObj = callSelector(_currInvokeSuperClsName, ORIGSelectorName, [JSValue valueWithObject:argList inContext:_context], [JSValue valueWithObject:@{@"__obj" : slf, @"__realClsName" : @"" } inContext:_context], NO ); id __autoreleasing ret = formatJSToOC([JSValue valueWithObject:retObj inContext:_context]); [invocation setReturnValue:&ret]; return ; } }
把self与相应的参数都添加到一个集合中。
代码片段三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 NSArray *params = _formatOCToJSList(argList);const char *returnType = [methodSignature methodReturnType];... #define JP_FWD_RET_CALL_JS \ JSValue *fun = getJSFunctionInObjectHierachy(slf, JPSelectorName); \ JSValue *jsval; \ [_JSMethodForwardCallLock lock]; \ jsval = [fun callWithArguments:params]; \ [_JSMethodForwardCallLock unlock]; \ while (![jsval isNull] && ![jsval isUndefined] && [jsval hasProperty:@"__isPerformInOC" ]) { \ NSArray *args = nil ; \ JSValue *cb = jsval[@"cb" ]; \ if ([jsval hasProperty:@"sel" ]) { \ id callRet = callSelector(![jsval[@"clsName" ] isUndefined] ? [jsval[@"clsName" ] toString] : nil , [jsval[@"sel" ] toString], jsval[@"args" ], ![jsval[@"obj" ] isUndefined] ? jsval[@"obj" ] : nil , NO ); \ args = @[[_context[@"_formatOCToJS" ] callWithArguments:callRet ? @[callRet] : _formatOCToJSList(@[_nilObj])]]; \ } \ [_JSMethodForwardCallLock lock]; \ jsval = [cb callWithArguments:args]; \ [_JSMethodForwardCallLock unlock]; \ }
把包含self与调用的参数转换为js对象,getJSFunctionInObjectHierachy
获取对应的js重写的函数,直接调用callWithArgument
方法,执行函数。
以上部分我们发现handleBtn
的实现部分实际上是_JPhandleBtn
对应的方法的实现,调用的流程基本了解,而此时我们有疑问,Demo中handleBtn
具体的替换实现(见代码)是如何执行的呢?
1 2 var tableViewCtrl = JPTableViewController.alloc () .init () self.navigationController () .pushViewController_animated (tableViewCtrl, YES)
1.2 callSelector 1.2.1 入口 代码片段一:分析JSPatch.js的代码部分时我们发现会有如下一段代码,给js对象基类 Object 的 prototype 加上 c 成员,这样所有对象都可以调用到 c,为什么这么做可以查看原作者wiki详解
1 2 3 4 Object .defineProperty (Object .prototype , "__c ", {value : function (methodName) { ... }, configurable :false , enumerable : false });
因此我们只需要关注__c
方法的具体实现,分析发现它的核心实现是
1 2 3 4 return function () {var args = Array .prototype.slice.call(arguments)return _methodFunc(self .__obj, self .__clsName, methodName, args, self .__isSuper)}
查看_methodFunc
的代码,最终定位_OC_callI
,_OC_callC
两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var _methodFunc = function(instance, clsName, methodName, args , isSuper, isPerformSelector) {var selectorName = methodNameif (!isPerformSelector) { methodName = methodName.replace (/__/g , "-" ) selectorName = methodName.replace (/_/g , ":" ).replace (/-/g , "_" ) var marchArr = selectorName.match (/:/g ) var numOfArgs = marchArr ? marchArr.length : 0 if (args .length > numOfArgs) { selectorName += ":" } } var ret = instance ? _OC_callI(instance, selectorName, args , isSuper): _OC_callC(clsName, selectorName, args ) return _formatOCToJS(ret )}
由startEngine可知,_OC_callI
,_OC_callC
两个方法为注入到context的全局的方法,因此就定位到callSelector
。以上分析了callSelector
的入口,下面主要分析它的具体实现。
1 2 3 4 5 6 context[@"_OC_callI" ] = ^id (JSValue *obj, NSString *selectorName, JSValue *arguments, BOOL isSuper) { return callSelector(nil , selectorName, arguments, obj, isSuper); }; context[@"_OC_callC" ] = ^id (NSString *className, NSString *selectorName, JSValue *arguments) { return callSelector(className, selectorName, arguments, nil , NO ); };
1.2.2 实现 代码片段一:
1 2 3 4 5 6 7 8 9 10 11 if (instance) { instance = formatJSToOC(instance); if (!instance || instance == _nilObj) return @{@"__isNil" : @(YES )}; } id argumentsObj = formatJSToOC(arguments); if (instance && [selectorName isEqualToString:@"toJS" ]) { if ([instance isKindOfClass:[NSString class]] || [instance isKindOfClass:[NSDictionary class]] || [instance isKindOfClass:[NSArray class]] || [instance isKindOfClass:[NSDate class]]) { return _unboxOCObjectToJS(instance); } }
把js对象与参数转换为OC对象
代码片段二:
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 26 27 28 if (isSuper) {NSString *superSelectorName = [NSString stringWithFormat:@"SUPER_%@" , selectorName];SEL superSelector = NSSelectorFromString (superSelectorName); Class superCls; if (clsDeclaration.length ) { NSDictionary *declarationDict = convertJPDeclarationString(clsDeclaration); NSString *defineClsName = declarationDict[@"className" ]; Class defineClass = NSClassFromString (defineClsName); superCls = defineClass ? [defineClass superclass] : [cls superclass]; } else { superCls = [cls superclass]; } Method superMethod = class_getInstanceMethod(superCls, selector); IMP superIMP = method_getImplementation(superMethod); class_addMethod(cls, superSelector, superIMP, method_getTypeEncoding(superMethod)); NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@" , selectorName];JSValue *overideFunction = _JSOverideMethods[superCls][JPSelectorName]; if (overideFunction) { overrideMethod(cls, superSelectorName, overideFunction, NO , NULL ); } selector = superSelector; }
判断是否是父类的方法,走父类的方法的实的实现
代码片段三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 NSInvocation *invocation;NSMethodSignature *methodSignature;if (!_JSMethodSignatureCache) {_JSMethodSignatureCache = [[NSMutableDictionary alloc]init]; } if (instance) { ... invocation= [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:cls]; } [invocation setSelector:selector]; ... [invocation invoke]; ... return returnValue;
封装NSInvocation
并执行,返回处理的结果
补充说明 上一篇博文中预留一个问题:4.3.1 中为什么需要加入参数个数的说明呢? 如下代码:
*typeDescStr = [@"@@:" mutableCopy]; 1 for (int i = 0; i < numberOfArg; i ++) {
[typeDescStr appendString:@"@"];
}
overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:NSUTF8StringEncoding]);**
需要根据传递过来的参数的个数声称方法的签名。
JSPatch核心的代码分析的部分已经完成,可以参考我的两篇博文,JSPatch源码学习(一) JSPatch源码学习(二) 部分细节问题未作具体的分析,例如内存,JPBoxing
,JPExtension
等,有兴趣可以关注我后期的该主题的博文。本人还在不断的学习积累中,有问题欢迎及时指出,谢谢!