认识JavaScriptCore.framework
githubDemo:https://github.com/wangjinshan/JSCDemo
项目演示正文JavaScriptCore.framework 是苹果在ios7之后新增的框架,是对 UIWebView的一次封装,方便开发者使用,使用JavaScriptCore.framework可以轻松实现 ios与js的交互
JavaScriptCore的组成JavaScriptCore中主要的类
详细介绍
1, JSContext --- 在OC中创建JavaScript运行的上下文环境
JSValue --- JavaScript中的变量和方法,可以转成OC数据类型,每个JSValue都和JSContext相关联并且强引用context
OC 和 js 数据对照表
3, JSExport --- JS调用OC中的方法和属性写在继承自JSExport的协议当中,OC对象实现自定义的协议
官方给的例子 //@textblock @protocol MyClassJavaScriptMethods <JSExport> - (void)foo; @end // 方法实现 @interface MyClass : NSObject <MyClassJavaScriptMethods> - (void)foo; - (void)bar; @end //@/textblock4, JSManagedValue --- JS和OC对象的内存管理辅助对象,主要用来保存JSValue对象,解决OC对象中存储js的值,导致的循环引用问题
// 初始化 - (instancetype)initWithValue:(JSValue *)value; + (JSManagedValue *)managedValueWithValue:(JSValue *)value; + (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner NS_AVAILABLE(10_10, 8_0);#p#分页标题#e#JSManagedValue本身只弱引用js值,需要调用JSVirtualMachine的addManagedReference:withOwner:把它添加到JSVirtualMachine中,这样如果JavaScript能够找到该JSValue的Objective-C owner,该JSValue的引用就不会被释放。
5, JSVirtualMachine --- JS运行的虚拟机,有独立的堆空间和垃圾回收机制,运行在不同虚拟机环境的JSContext可以通过此类通信。
// 初始化 - (instancetype)init; // 添加 - (void)addManagedReference:(id)object withOwner:(id)owner; // 移除 - (void)removeManagedReference:(id)object withOwner:(id)owner;到此 JavaScriptCore.framework 能够使用的基本api已经介绍完毕
下面我们以简单集成SMSDK为 实际例子进行讲解
首先创建必要的三个文件 SMSDK.html/ SMSDK.cc SMSDK.js 在viewDidLoad中创建webview进行加载SDMSDK.html
js中方法的实现
我们同样选择在网页加载完成的方法中进行方法调用
-(void) webViewDidFinishLoad:(UIWebView *)webView { [self initSMSDK]; } 实现 方法 -(void) initSMSDK { // 创建上下文 // 1.这种方式需要传入一个JSVirtualMachine对象,如果传nil,会导致应用崩溃的。 JSVirtualMachine *JSVM = [[JSVirtualMachine alloc] init]; JSContext *jscontext = [[JSContext alloc] initWithVirtualMachine:JSVM]; 2.这种方式,内部会自动创建一个JSVirtualMachine对象,开题报告,可以通过JSCtx.virtualMachine // 看其是否创建了一个JSVirtualMachine对象。 JSContext *jscontext = [[JSContext alloc] init]; /**********以上的方法经过测试都不好使*************/ 正确的姿势 1, 创建上下文,意思就是让oc和js 同处于一个环境,方便进行方法调用 JSContext *jscontext = [self.mywebview valueForKeyPath:@'documentView.webView.mainFrame.javaScriptContext']; 2 参数接受 注意一定输传递方法的名字,不要加()// 和webview的调用方法的区别 JSValue *jsvalue = jscontext[@'initSDK']; // 返回的是方法的名字和内容 3 调用函数/传递参数 JSValue *initData = [jsvalue callWithArguments:@[@'从oc中传递第一个参数进去']]; 接收到的字典对象进行解析,并调用SMSDK的初始化方法 NSDictionary *dic = [initData toDictionary]; NSString *appkey = dic[@'appkey'][@'appkey']; NSString *appSecrect = dic[@'appSecrect'][@'appSecrect']; [SMSSDK registerApp:appkey withSecret:appSecrect]; }我们通过打印上面的 appkey 等参数就知道 实现了ios调用 js的方法
js调用 ios可以通过两种方式在JavaScript中调用Objective-C:
Blocks: 对应JS函数
JSExport协议: 对应JS对象
我们先实现 block的方法
Block的方法
首先在html中 写一个button的点击方法
getCode()方法就是将来 ios 中需要注册的方法体, 里面是对js返回参数方法的调用 相当于 getCode('返回给js的参数,在参数列表中调用就可以');
过程和上面一样 // 1,获取上下文 // 下文weakSelf.jsContext 中已经在属性中保存jscontext JSContext *_jsContext = [self.mywebview valueForKeyPath:@'documentView.webView.mainFrame.javaScriptContext']; // 2 异常捕获机制 _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) { context.exception = exception; //别忘了给exception赋值,否则JSContext的异常信息为空 NSLog(@'---错误数据的处理----%@',exception); }; __weak typeof (self) weakSelf = self; // 防止循环引用就加上 __weak _jsContext[@'getCode'] = ^ (){ NSArray *arr =[JSContext currentArguments]; // 获取当前的上下文,然后加载 js返回的参数列表 for (id objc in arr) { weakSelf.phoneNumber = objc; [SMSSDK getVerificationCodeByMethod:SMSGetCodeMethodSMS phoneNumber:objc zone:@'86' customIdentifier:nil result:^(NSError *error) { JSContext *jscontext = weakSelf.jsContext; if (!error) { // ios回调 js的代码 并传递参数给 js JSValue *jsvalue = jscontext[@'getCodeCallBack']; // 注入方法 [jsvalue callWithArguments:@[@'获取短信验证码成功']]; // 调用方法 } else { JSValue *jsvalue = jscontext[@'getCodeCallBack']; // 注入方法 [jsvalue callWithArguments:@[@'获取验证码失败']]; } }]; } };#p#分页标题#e#通过上面的方法我们知道, block 方法 ios 给 js传递的是方法 ,如果传递 对象则需要用到 协议的方法
首先我们来看一个最简单的例子 :
在 js 上写一个 点击事件,然后调用 ios 的方法
在 js中
在viewController.m中,定义协议
/** * 实现js代理,js调用ios的入口就在这里 */ @protocol JSDelegate <JSExport> - (void)getImage:(id)parameter;// 这个方法就是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 getImage()方法 @end签订协议: 注意这里的协议不需要实现,在外部也不需要设置代理,直接实现就可以
@interface ViewController ()<UIWebViewDelegate,JSDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate,JSOCViewControllerExport>在网页加载完成的代理中实现, 上下文
self.jsContext = [self.myWebView valueForKeyPath:@'documentView.webView.mainFrame.javaScriptContext']; // 测试处理 self.jsContext[@'objc'] = self;//挂上代理 iosDelegate是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 iosDelegate self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){ context.exception = exception; NSLog(@'js方法写错了 错误的信息都会在此处输出:%@',exception); };最后一步就是实现 在 js中声明的协议方法
// 协议实现 - (void)getImage:(id)parameter { NSArray *arr =[JSContext currentArguments]; for (id objc in arr) { NSLog(@'=-----%@',[objc toDictionary]); } [self beginOpenPhoto]; // 相机的处理, }实现相机的方法,很简单, 里面的数据传递和上文中说到的一个样子,这里将不再赘述.
到此, 采用协议实现 js 调用 ios的方法基本完成
关于内存泄漏,循环引用的问题 注意不要在 block中直接 引用外部 强引用的对象就可以
__weak typeof (self) weakSelf = self; // 防止循环引用就加上 __weak _jsContext[@'getCode'] = ^ (id oc){ NSArray *arr =[JSContext currentArguments]; // 获取当前的上下文 for (id objc in arr) { weakSelf.phoneNumber = objc; 这个 weakSelf 的处理就okgithubDemo:https://github.com/wangjinshan/JSCDemo