iOS-正確獲取UIWebView的JSContext

前言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

相關文章
相關標籤/搜索