栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > iOS

iOS中使用JSPatch框架使Objective-C与JavaScript代码交互

iOS 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

iOS中使用JSPatch框架使Objective-C与JavaScript代码交互

JSPatch是GitHub上一个开源的框架,其可以通过Objective-C的run-time机制动态的使用Javascript调用与替换项目中的Objective-C属性与方法。其框架小巧,代码简洁,并且通过系统的JavascriptCore框架与Objective-C进行交互,这使其在安全性和审核风险上都有很强的优势。Git源码地址:https://github.com/bang590/JSPatch。

一、从一个官方的小demo看起

通过cocoapods将JSPath集成进一个Xcode工程中,在AppDelegate类的中编写如下代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  //开始初始化引擎
  [JPEngine startEngine];
  //读取js文件
  NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
  NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
  //运行js文件
  [JPEngine evaluatescript:script];
  self.window = [[UIWindow alloc]initWithframe:[UIScreen mainScreen].bounds];
  self.window.rootViewController = [[ViewController alloc]init];
  [self.window addSubview:[self genView]];
  [self.window makeKeyAndVisible];
  return YES;
}

- (UIView *)genView
{
  UIView * view= [[UIView alloc] initWithframe:CGRectMake(0, 0, 320, 320)];
  view.backgroundColor = [UIColor redColor];
  return view;
}

在工程中添加一个js文件,编写如下:

  

 require('UIView, UIColor, UILabel')
  //要替换函数的类
  defineClass('AppDelegate', {
      //替换函数
 //要替换函数的名称
 genView: function() {
   var view = self.ORIGgenView();
   view.setBackgroundColor(UIColor.greenColor())
   var label = UILabel.alloc().initWithframe(view.frame());
   label.setText("JSPatch");
   label.setTextAlignment(1);
   view.addSubview(label);
   return view;
      }
  });

运行工程,可以看到genView方法被替换成了js文件中的方法,原本红色的视图被修改成了绿色。

二、使用Javascript代码向Objective-C中修改或添加方法

JSPatch引擎中支持3中方式进行Javascript代码的调用,分别是使用Javascript字符串进行代码运行,读取本地的Javascript文件进行代码运行和获取网络的Javascript文件进行代码运行。例如,如果想要通过Javascript代码在项目中弹出一个警告框,在Objective-C代码中插入如下代码:

- (void)viewDidLoad {
  [super viewDidLoad];
  // ‘'符用于进行换行
  [JPEngine evaluatescript:@"
   var alertView = require('UIalertView').alloc().init();
   alertView.setTitle('alert');
   alertView.setMessage('alertView from js'); 
   alertView.addButtonWithTitle('OK');
   alertView.show(); 
   "];
}

开发者也可以动态在Objective-C类文件中添加方法,例如在ViewController类中编写如下:

- (void)viewDidLoad {
  [super viewDidLoad];
  self.view.backgroundColor = [UIColor whiteColor];
  [JPEngine startEngine];
  NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
  NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
  [JPEngine evaluatescript:script];
  [self performSelectorOnMainThread:@selector(creatView) withObject:nil waitUntilDone:nil];
}

Javascript文件代码如下:

 require('UIView, UIColor, UILabel')
  defineClass('ViewController', {
      // replace the -genView method
 creatView: function() {
   var view = UIView.alloc().initWithframe({x:20, y:20, width:100, height:100});
   view.setBackgroundColor(UIColor.greenColor());
   var label = UILabel.alloc().initWithframe({x:0, y:0, width:100, height:100});
   label.setText("JSPatch");
   label.setTextAlignment(1);
   view.addSubview(label);
 self.view().addSubview(view)
      }
  });

除了上面的代码,在ViewController.m文件中没有编写任何其他的方法,运行工程,可以看到程序并没有崩溃,ViewController执行了creatView方法。

通过上面的示例,我们发现使用JSPatch可以做一些十分有趣的事。对于iOS应用来说,通过官方渠道AppStore进行应用程序的发布要通过人工审核,有时这个审核周期会非常长,如果在开发者在编写代码时留下了一些小漏洞,应用一旦上线,若要修改掉这个bug就十分艰难了。有了JSPatch,我们可以想象,如果可以定位到线上应用有问题的方法,使用JS文件来修改掉这个方法,这将是多么cool的一件事,事实上,JSPatch的主要用途也是可以实现线上应用极小问题的hotfix。

三、Javascript与Objective-C交互的基础方法

要使用JSPatch来进行Objective-C风格的方法编写,需要遵守一些Javascript与Objective-C交互的规则。

1.在Javascript文件中使用Objective-C类

在编写Javascript代码时如果需要用到Objective-C的类,必须先对这个类进行require引用,例如,如果需要使用UIView这个类,需要在使用前进行如下引用:

require('UIView')

同样也可以一次对多个Objective-C类进行引用:

require('UIView, UIColor, UILabel')

还有一种更加简便的写法,直接在使用的时候对其进行引用:

require('UIView').alloc().init()

2.在Javascript文件中进行Objective-C方法的调用

在进行Objective-C方法的调用时,分为两种,一种是调用类方法,一种是调用类的对象方法。

调用类方法:通过类名打点的方式来调用类方法,格式类似如下,括号内为参数传递:

UIColor.redColor()

调用实例方法:通过对象打点的方式调用类的实例方法,格式如下,括号内为参数传递:

view.addSubview(label)

对于Objective-C中的多参数方法,转化为Javascript将参数分割的位置以_进行分割,参数全部放入后面的括号中,以逗号分割,示例如下:

view.setBackgroundColor(UIColor.colorWithRed_green_blue_alpha(0,0.5,0.5,1))
对于Objective-C类的属性变量,在Javascript中只能使用getter与setter方法来访问,示例如下:

label.setText("JSPatch")

提示:如果原Objective-C的方法中已经包含了_符号,则在Javascript中使用__代替。

3.在Javascript中操作与修改Objective-C类

JSPatch的最大应用是在应用运行时动态的操作和修改类。

重写或者添加类的方法:

在Javascript中使用defineClass来定义和修改类中的方法,其编写格式如下所示:


defineClass(classDeclaration, instanceMethods, classMethods)

示例如下:

defineClass('ViewController', {
      // replace the -genView method
 newFunc: function() {
   //编写实例方法
   self.view().setBackgroundColor(UIColor.redColor())
 }
  
      },{

 myLoad:function(){
   //编写类方法
 }

      }
      )

如果在重写了类中的方法后要调用原方法,需要使用ORIG前缀,示例如下:

defineClass('ViewController', {
      // replace the -genView method
 viewDidLoad: function() {
   //编写实例方法
   self.ORIGviewDidLoad()
 }
  
      }
      )

对于Objective-C中super关键字调用的方法,在Javascript中可以使用self.super()来调用,例如:

defineClass('ViewController', {
      // replace the -genView method
 viewDidLoad: function() {
   //编写实例方法
   self.super().viewDidLoad()
 }
  
      }
      )

同样JSPatch也可以为类添加临时属性,用于在方法间参数传递,使用set_Prop_forKey()来添加属性,使用getProp()来获取属性,注意,JSPatch添加的属性不能使用Objective-C的setter与getter方法访问,如下:

defineClass('ViewController', {
      // replace the -genView method
 viewDidLoad: function() {
   //编写实例方法
   self.super().viewDidLoad()
   self.setProp_forKey("JSPatch", "data")
 },
 touchesBegan_withEvent(id,touch){
   self.getProp("data")
   self.view().setBackgroundColor(UIColor.redColor())
 }
  
      }
      )

关于为类添加协议的遵守,和Objective-C中遵守协议的方式一致,如下:

defineClass("ViewController2: UIViewController ", {
      viewDidAppear: function(animated) {
      var alertView = require('UIalertView')
      .alloc()
      .initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles(
     "alert",
     "content",
     self,
     "OK",
     null
     )
      alertView.show()
      },
      alertView_clickedButtonAtIndex:function(alertView, buttonIndex) {
      console.log('clicked index ' + buttonIndex)
      }
      })

四、Javascript与Objective-C交互的几种常用类型

1.结构体

在Objective-C代码中,我们经常会使用到结构体,JSPatch中原生支持的结构体有如下几种:CGPoint,CGSize,CGRect,NSRange。并且这几种结构体在进行界面操作时也会经常使用到。

对于CGRect类型,Javascript使用如下代码创建:

  var view = require('UIView').alloc().init()
  view.setframe({x:100,y:100,width:100,height:100})

对于CGPoint类型,Javascript使用如下代码创建:

   

view.setCenter({x:200,y:200})

对于CGSize类型,Javascript使用如下代码创建:

  var size = {width:200,height:200}
  view.setframe({x:100,y:100,width:size.width,height:size.height})

对于NSRange类型,Javascript使用如下代码创建:

  var range = {location: 0, length: 1}

2.选择器Selector

对于Objective-C中的方法选择器Selector,在Javascript中使用字符串的形式创建,例如:

self.performSelector_withObject("func:", 1)

3.关于空对象

在Javascript中,null与undefined都对应于Objective-C中的nil,Objective-C中的NSNull空对象,在Javascript中使用nsnull来代替。

4.在Objective-C与Javascript中进行block的交互

在Javascript与Objective-C进行block交互有两种方式,一种是在Javascript文件中调用Objective-C中的block,一种是将Javascript文件中的函数块作为block参数传递给Objective-C。

在Javascript文件中使用Objective-C中的block十分简单,因为Javascript中没有block的概念,Objective-C会被自动转换为函数,示例如下:

Objective-C:

typedef void(^block)(NSString * str);
@interface ViewController ()
@end
@implementation ViewController
-(block)getBlock{
  block block = ^(NSString * str){NSLog(@"%@",str);};
  return block;
}
@end

Javascript:

defineClass("ViewController", {
      viewDidAppear: function(animated) {
var func = self.getBlock()
 func("123")
      }
      })

在Javascript文件中将func作为参数block传递给Objective-C就复杂一些,需要使用block()方法进行包装,例如:

Objective-C:

@interface ViewController ()
@end
@implementation ViewController

-(void)run:(void(^)(NSString * str))block{
  block(@"123");
}
@end

Javascript:

defineClass("ViewController", {
      viewDidAppear: function(animated) {
      //run 方法中需要传入一个block
      self.run(block("NSString*",function(str){console.log(str)}))
      }
      })

在使用block()方法对Javascript中的Func进行包装时,block(param1,param2)有两个参数,第1个参数设置func中的参数类型,如果有多个参数,使用逗号分割;第2个参数为func函数体。

注意:在block()包装的func中不可以使用self指针,如果需要使用self,需要在block外进行临时变量的转换,示例如下:

defineClass("ViewController", {
      viewDidAppear: function(animated) {
      //run 方法中需要传入一个block
      var slf = self
      self.run(block("NSString*",
function(str){
console.log(str)
slf.log(str)
}))
      }
      })

在Javascript中分别使用__weak()与__strong来声明弱引用与强引用对象,例如:

 var slf = __weak(self)
 var stgSef = __strong(self)

5.关于GCD与枚举

在JSPatch中,可以使用如下Javascript代码来调用GCD方法:

//阻塞当前线程一定时间
dispatch_after(1.0, function(){ 
})
//为主线程添加异步任务
dispatch_async_main(function(){ 
})
//为主线程添加同步任务
dispatch_sync_main(function(){ 
})
//向全局队列中添加任务
dispatch_async_global_queue(function(){ 
})
JSPatch中不可以直接使用Objective-C中定义的枚举,但是可以用其枚举的真实值进行传递。例如:

//UIControlEventTouchUpInside的值是1<<6
btn.addTarget_action_forControlEvents(self, "handleBtn", 1<<6);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/167326.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号