JSPatch
原理:在APP启动的时候,通过JavaScrptCore来执行编写的JavaScript脚本,利用OC的运行时特性来修改类的方法和属性。
1:执行JS脚本
#import "AppDelegate.h" // 导入JSPatch框架 #import <JSPatch/JPEngine.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Bugly startWithAppId:BuglyAppId]; [JPEngine startEngine]; // 加载本地的JS文件 // NSString *path = [[NSBundle mainBundle] pathForResource:@"firstChange" ofType:@"js"]; // NSString *script = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; // [JPEngine evaluateScript:script]; // 记载请求的JS文件 [[FFNetworkHelper share] GetFile:[NSString stringWithFormat:@"xxx/%@", @"firstChange.js"] successBlock:^(id _Nonnull responseObject) { [JPEngine evaluateScript:responseObject]; } failuerBlock:^(NSString * _Nonnull str) { #if DEBUG FFShowInfo(@"JS 修复文件下载失败"); #endif }]; self.window = [[UIWindow alloc] initWithFrame:MainScreenBounds];
s self.window.rootViewController = [self FF_GetTabBarController]; [self.window makeKeyAndVisible]; return YES; }
2: 在动态修改OC类的方法和属性时的核心方法 defineClass('className', instanceMethod, classMethod)
className--要覆盖的类名,字符串类型
instanceMethod--实例方法
classMethod--类方法(要修改类方法,必须有实例方法,如果实例方法省略,就会默认为instanceMethod)
/* ##API defineClass(classDeclaration, instanceMethods, classMethods) @param classDeclaration: class name, super classname and protocols @param instanceMethods: instance methods you want to override or add @param classMethods: class methods you want to override or add 1.Use _ to separate multiple params: 使用_链接多个参数 2.Use __ to represent _ in the Objective-C method name: 如果原来的方法名中有_用__代替 3.Call original method by adding prefix ORIG to the method name. 如果想调用OC中对应的初始方法,添加前缀ORIG 4.Super / Property / Member variables a: Use self.super() to call super methods. 使用self.super()调用super method b: Call methods to get / set existing property. 通过get / set 方法来操作存在的属性 // JS defineClass("JPTableViewController", { viewDidLoad: function() { var data = self.data() //get property value self.setData(data.toJS().push("JSPatch")) //set property value }, }) c: Use getProp() and setProp_forKey() to get / set new property 通过getProp() 和 setProp_forKey()来get / set 一个新属性 // JS defineClass("JPTableViewController", { init: function() { self = self.super().init() self.setProp_forKey("JSPatch", "data") //add new property (id data) return self; } viewDidLoad: function() { var data = self.getProp("data") //get the new property value }, }) d: Use valueForKey() and setValue_forKey() to get / set member variables 成员变量操作 // JS defineClass("JPTableViewController", { viewDidLoad: function() { var data = self.valueForKey("_data") //get member variables self.setValue_forKey(["JSPatch"], "_data") //set member variables }, }) 5.You can add new methods to the existing Class, if you want to call the new method in Objective-C, all the params type is id. 在已经存在的类中添加一个新方法,在OC中回调是,参数是id类型 6.Special types Use hash object to represent CGRect / CGPoint / CGSize / NSRange // JS var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100}) view.setCenter({x: 10, y: 10}) view.sizeThatFits({width: 100, height:100}) var x = view.frame.x var range = {location: 0, length: 1} */ /// 要用到的类,用require导入 require('UIView, NSString, NSDate, NSLog') /// 要重载的类, 修改viewDidLoad方法 defineClass( 'FFBaseViewController', { /// 实例方法 viewDidLoad: function(){ /// self.super() 回调 super method self.super().viewDidLoad(); /// 要执行的操作 ... }; }) /// 要重载的类, 调用viewDidLoad方法 defineClass( 'FFBaseViewController', { /// 实例方法 viewDidLoad: function(){ /// self.super() 回调 parent method self.ORIGviewDidLoad(); /// 要执行的操作 ... }; }) /// 要重载的类, 修改viewDidLoad方法, 添加一个类方法FFPrint defineClass( 'FFBaseViewController', { /// 实例方法 viewDidLoad: function(){ /// self.super() 回调 parent method self.super().viewDidLoad(); /// 要执行的操作 ... }; }, { /// 类方法 FFPrint: functon(){ NSLog('类方法'); } } )