1.前言 前一段时间在公司做了一个iOS热补丁的模块,就用到了JSPatch框架 ,期间有了解过一些关于框架的源码分析的博客:1.JSPatch学习:JSPatch核心和实现原理分析
2.JSPatch defineProtocol部分实现详解 思考再三还是决定自己调试了解一下整体的实现机制。
2.准备工具
3.项目文件 3.1项目文件图
3.2具体文件分析
JPEngine.m 核心的Native端实现。
JSpatch.js 核心的js端实现。
Extensions 扩展的方法提供给js端调用,内部是OC实现。
Loader 一套热补丁动态更新补丁脚本的客户端实现,需要配合服务端才能实现整个更新框架。(本文不对此作多赘述)
4.调试分析 4.1调用代码 1 2 3 4 [JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js" ];NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil ];[JPEngine evaluateScript:script];
4.2 JPEngine初始化
我们来看看具体的代码实现,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + (void )startEngine { if (![JSContext class] || _context) { return ; } JSContext *context = [[JSContext alloc] init]; context[@"_OC_defineClass" ] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) { return defineClass(classDeclaration, instanceMethods, classMethods); }; context[@"_OC_defineProtocol" ] = ^(NSString *protocolDeclaration, JSValue *instProtocol, JSValue *clsProtocol) { return defineProtocol(protocolDeclaration, instProtocol,clsProtocol); }; }
_content 为JSContect的实例,根据苹果官方文档 :
我们知道JSContext为js的执行环境。因此例如:context[@"_OC_defineProtocol"]=block实现
这样的调用就很好的理解为为js的上下文注入了全局的_OC_defineProtocol
方法,而具体的实现对应着Native端的block的实现。
1 2 3 4 5 6 7 8 9 NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"JSPatch" ofType:@"js" ];NSAssert (path, @"can't find JSPatch.js" );NSString *jsCore = [[NSString alloc] initWithData:[[NSFileManager defaultManager] contentsAtPath:path] encoding:NSUTF8StringEncoding ]; if ([_context respondsToSelector:@selector (evaluateScript:withSourceURL:)]) { [_context evaluateScript:jsCore withSourceURL:[NSURL URLWithString:@"JSPatch.js" ]]; } else { [_context evaluateScript:jsCore]; }
加载核心的JSPatch.js代码完成初始化,具体js代码后续具体调用再作分析。
4.3具体修复代码 我们来看看Demo的修复代码:
1 2 3 4 5 6 defineClass('JPViewController ', { handleBtn: function(sender ) { var tableViewCtrl = JPTableViewController.alloc() .init() self.navigationController() .pushViewController_animated(tableViewCtrl , YES) } })
调试看看执行步骤:
4.3.1 首先执行global.defineClass (js端) 1 global.defineClass = function (declaration, properties, instMethods, clsMethods)
我们断点到var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods)
处,查看局部变量表 会发现newInstMethods newClsMethods
均被赋值,且其中每个实例或类方法的js对象被修改添加参数的个数的说明,只是好奇为什么需要加入参数个数的说明呢???
4.3.2 执行_OC_defineClass
(js端 -> Native端) 1 2 3 context[@"_OC_defineClass" ] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) { return defineClass (classDeclaration, instanceMethods, classMethods) ; };
实际执行的是Native的 static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
方法,以下分代码片段解析。
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 29 30 31 32 33 34 35 36 37 38 NSScanner *scanner = [NSScanner scannerWithString:classDeclaration]; NSString *className; NSString *superClassName; NSString *protocolNames; [scanner scanUpToString:@":" intoString:&className]; if (!scanner.isAtEnd ) { scanner.scanLocation = scanner.scanLocation + 1 ; [scanner scanUpToString:@"<" intoString:&superClassName]; if (!scanner.isAtEnd ) { scanner.scanLocation = scanner.scanLocation + 1 ; [scanner scanUpToString:@">" intoString:&protocolNames]; } } if (!superClassName) superClassName = @"NSObject" ; className = trim(className); superClassName = trim(superClassName); NSArray *protocols = [protocolNames length] ? [protocolNames componentsSeparatedByString:@"," ] : nil ; Class cls = NSClassFromString (className); if (!cls) { Class superCls = NSClassFromString (superClassName); if (!superCls) { NSCAssert (NO , @"can't find the super class %@" , superClassName); return @{@"cls" : className}; } cls = objc_allocateClassPair(superCls, className.UTF8String , 0 ); objc_registerClassPair(cls); } if (protocols.count > 0 ) { for (NSString * protocolName in protocols) { Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:NSUTF8StringEncoding ]); class_addProtocol (cls, protocol); } }
通过传递的classDeclaration解析相应的className,superClassName,protocols,
1 2 3 4 5 6 7 8 9 10 11 cls = objc_allocateClassPair(superCls, className.UTF8String , 0 ); objc_registerClassPair(cls); if (protocols.count > 0 ) { for (NSString * protocolName in protocols) { Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:NSUTF8StringEncoding ]); class_addProtocol (cls, protocol); } }
1.运行期间创建一个新类,并完成注册. 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 for (int i = 0 ; i < 2 ; i ++) { BOOL isInstance = i == 0 ; JSValue *jsMethods = isInstance ? instanceMethods: classMethods; Class currCls = isInstance ? cls: objc_getMetaClass(className.UTF8String ); NSDictionary *methodDict = [jsMethods toDictionary]; for (NSString *jsMethodName in methodDict.allKeys ) { JSValue *jsMethodArr = [jsMethods valueForProperty:jsMethodName]; int numberOfArg = [jsMethodArr[0 ] toInt32]; NSString *selectorName = convertJPSelectorString(jsMethodName); if ([selectorName componentsSeparatedByString:@":" ].count - 1 < numberOfArg) { selectorName = [selectorName stringByAppendingString:@":" ]; } JSValue *jsMethod = jsMethodArr[1 ]; if (class_respondsToSelector(currCls, NSSelectorFromString (selectorName))) { overrideMethod(currCls, selectorName, jsMethod, !isInstance, NULL ); } else { BOOL overrided = NO ; for (NSString *protocolName in protocols) { char *types = methodTypesInProtocol(protocolName, selectorName, isInstance, YES ); if (!types) types = methodTypesInProtocol(protocolName, selectorName, isInstance, NO ); if (types) { overrideMethod(currCls, selectorName, jsMethod, !isInstance, types); free(types); overrided = YES ; break ; } } if (!overrided) { if (![[jsMethodName substringToIndex:1 ] isEqualToString:@"_" ]) { NSMutableString *typeDescStr = [@"@@:" mutableCopy]; for (int i = 0 ; i < numberOfArg; i ++) { [typeDescStr appendString:@"@" ]; } overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:NSUTF8StringEncoding ]); } } } } }
1 2 3 4 5 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" class_addMethod(cls, @selector (getProp:), (IMP)getPropIMP, "@@:@" ); class_addMethod(cls, @selector (setProp:forKey:), (IMP)setPropIMP, "v@:@@" ); #pragma clang diagnostic pop
添加类的getProp:``setProp:forKey:
的方法及实现。
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 static void overrideMethod(Class cls, NSString *selectorName, JSValue *function, BOOL isClassMethod, const char *typeDescription){ SEL selector = NSSelectorFromString (selectorName); if (!typeDescription) { Method method = class_getInstanceMethod(cls, selector); typeDescription = (char *)method_getTypeEncoding(method); } IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL ; IMP msgForwardIMP = _objc_msgForward; #if !defined(__arm64__) if (typeDescription[0 ] == '{' ) { NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:typeDescription]; if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES" ].location != NSNotFound ) { msgForwardIMP = (IMP)_objc_msgForward_stret; } } #endif class_replaceMethod(cls, selector, msgForwardIMP, typeDescription); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" 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@:@" ); } #pragma clang diagnostic pop if (class_respondsToSelector(cls, selector)) { NSString *originalSelectorName = [NSString stringWithFormat:@"ORIG%@" , selectorName]; SEL originalSelector = NSSelectorFromString (originalSelectorName); if (!class_respondsToSelector(cls, originalSelector)) { class_addMethod(cls, originalSelector, originalImp, typeDescription); } } NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@" , selectorName]; SEL JPSelector = NSSelectorFromString (JPSelectorName); _initJPOverideMethods(cls); _JSOverideMethods[cls][JPSelectorName] = function; class_addMethod(cls, JPSelector, msgForwardIMP, typeDescription); }
核心的替换添加方法实现的方法:
把原始的方法的实现替换为_objc_msgForward
,即该方法的调用会走消息转发的路径.
类的forwardInvocation:
方法的实现被替换为JPForwardInvocation
的实现
添加的方法ORIGforwardInvocation
指向原始的实现IMP.
添加的方法ORIG+selector
指向原始的实现的IMP.
添加_JP+selector
指向新的函数的实现.
上述代码只是讲解了替换添加方法的实现,而新的实现方法是一个个js的对象,如何关联到Native端的调用?后续会继续更新,分析 调用核心方法JPForwardInvocation,JPExtension等其他部分的实现,对于上述的分析有问题处欢迎及时指出,谢谢!
补充:4.3.1 中为什么需要加入参数个数的说明呢,与方法的签名有关,具体下次更新作分析!!!