iOS-正确获取UIWebView的JSContext

前言

iOS7后苹果开发的JavaScriptCore框架,方便开发者原生逻辑与JS逻辑交互,但是苹果并没有开放获取JSContext环境对象的方法,之前都是通过KVC的方式获取JSContext环境对象,[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]。本文是借鉴UIWebView+TS_JavaScriptContext第三方库,并提出自己的一些看法或者说是想法吧。

 

JavaScript和Objective-C交互存在的深坑

(1)内存管理,使用JSExport协议注入对象,会造成强引用;

(2)还有就是什么时候注入对象是正确的;具体的原因参考这篇文章

(3)其实还有一点需要注意,JavaScript方法调用原生OC的方法是在子线程中执行的,若涉及UI逻辑,请自行切换至主线程操作。

 

但是,UIWebView+TS_JavaScriptContext的逻辑中使用了苹果私有API,parentFrame,导致采用此方法获取JSContext的应用被拒绝上架,因此我去掉原有的frame的判断。

#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

 

最后

网络上没有查找到如何规避使用WebFrame的parentFrame私有API的方法,因此不得不去掉原有第三方中的逻辑判断,本文所写的代码也还未运用至实际项目开发,因此我会在新版项目中尝试使用。

转载于:https://my.oschina.net/u/1450995/blog/912201

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值