前言java
iOS7后苹果开发的JavaScriptCore框架,方便开发者原生逻辑与JS逻辑交互,可是苹果并无开放获取JSContext环境对象的方法,以前都是经过KVC的方式获取JSContext环境对象,[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]。本文是借鉴UIWebView+TS_JavaScriptContext第三方库,并提出本身的一些见解或者说是想法吧。git
JavaScript和Objective-C交互存在的深坑github
(1)内存管理,使用JSExport协议注入对象,会形成强引用;web
(2)还有就是何时注入对象是正确的;具体的缘由参考这篇文章;网络
(3)其实还有一点须要注意,JavaScript方法调用原生OC的方法是在子线程中执行的,若涉及UI逻辑,请自行切换至主线程操做。框架
可是,UIWebView+TS_JavaScriptContext的逻辑中使用了苹果私有API,parentFrame,致使采用此方法获取JSContext的应用被拒绝上架,所以我去掉原有的frame的判断。async
#import <UIKit/UIKit.h> #import <JavaScriptCore/JavaScriptCore.h> @protocol DYLWebViewDelegate <UIWebViewDelegate> @optional - (void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext *)ctx; @end @interface UIWebView (JavaScriptContext) @property (strong, nonatomic, readonly) JSContext *javaScriptContext; @end #import "UIWebView+JavaScriptContext.h" #import <objc/runtime.h> @interface UIWebView (DYL_JavaScriptCore_Private) - (void)dyl_didCreateJavaScriptContext:(JSContext *)dyl_javaScriptContext; @end static NSHashTable *g_webViews = nil; static const void *kStorageJavaScriptContext = @"kStorageJavaScriptContext"; @implementation NSObject (JavaScriptContext) - (void)webView:(id)unused didCreateJavaScriptContext:(JSContext *)ctx forFrame:(id)frame { void (^notifyDidCreateJavaScriptContext)() = ^{ for (UIWebView *webView in g_webViews) { NSString *identifier = [NSString stringWithFormat:@"ts_jscWebView_%lu", (unsigned long)webView.hash]; [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"var %@ = '%@'", identifier, identifier]]; if ([ctx[identifier].toString isEqualToString:identifier]) { [webView dyl_didCreateJavaScriptContext:ctx]; return ; } } }; if ([NSThread mainThread]) { notifyDidCreateJavaScriptContext(); } else { dispatch_async(dispatch_get_main_queue(), notifyDidCreateJavaScriptContext); } } @end @implementation UIWebView (JavaScriptContext) + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ g_webViews = [NSHashTable weakObjectsHashTable]; }); id webView = [super allocWithZone:zone]; [g_webViews addObject:webView]; return webView; } - (void)dyl_didCreateJavaScriptContext:(JSContext *)dyl_javaScriptContext { objc_setAssociatedObject(self, kStorageJavaScriptContext, dyl_javaScriptContext, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if ([self.delegate respondsToSelector:@selector(webView:didCreateJavaScriptContext:)]) { id<DYLWebViewDelegate> delegate = (id<DYLWebViewDelegate>)self.delegate; [delegate webView:self didCreateJavaScriptContext:dyl_javaScriptContext]; } } - (JSContext *)javaScriptContext { return objc_getAssociatedObject(self, kStorageJavaScriptContext); } @end
最后ide
网络上没有查找到如何规避使用WebFrame的parentFrame私有API的方法,所以不得不去掉原有第三方中的逻辑判断,本文所写的代码也还未运用至实际项目开发,所以我会在新版项目中尝试使用。atom