繁体   English   中英

UIWebView JavaScript失去对iOS JSContext名称空间(对象)的引用

[英]UIWebView JavaScript losing reference to iOS JSContext namespace (object)

我一直在研究一个概念验证应用程序,它利用WebKit JavaScriptCore框架利用Objective C(iOS 7)和JavaScript之间的双向通信。 我终于能够按预期工作了,但是遇到了UIWebView失去对我通过JSContext创建的iOS对象的引用的情况。

该应用程序有点复杂,以下是基础知识:

  • 我正在iOS设备上运行Web服务器(CocoaHTTPServer)
  • UIWebView最初加载远程URL,稍后作为应用程序流的一部分重定向回localhost (想想OAuth)
  • 应用程序托管的HTML页面(在localhost上)具有应该与我的iOS代码通信的JavaScript

这是iOS方面,我的ViewController的.h

#import <UIKit/UIKit.h>
#import <JavaScriptCore/JavaScriptCore.h>

// These methods will be exposed to JS
@protocol DemoJSExports <JSExport>
-(void)jsLog:(NSString*)msg;
@end

@interface Demo : UIViewController <UserInfoJSExports, UIWebViewDelegate>
@property (nonatomic, readwrite, strong) JSContext *js;
@property (strong, nonatomic) IBOutlet UIWebView *webView;
@end

以及ViewController的.m的相关部分:

-(void)viewDidLoad {
    [super viewDidLoad];

    // Retrieve and initialize our JS context
    NSLog(@"Initializing JavaScript context");
    self.js = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    // Provide an object for JS to access our exported methods by
    self.js[@"ios"] = self;

    // Additional UIWebView setup done here...
}

// Allow JavaScript to log to the Xcode console
-(void)jsLog(str) {
    NSLog(@"JavaScript: %@", str);
}

这是(为了这个问题而简化)HTML / JS方面:

<html>
<head>
<title>Demo</title>
<script type="text/javascript">
    function setContent(c, noLog){
        with(document){
            open();
            write('<p>' + c + '</p>');
            close();
        }

        // Write content to Xcode console
        noLog || ios.jsLog(c);
    }    
</script>
</head>
<body onload="javascript:setContent('ios is: ' + typeof ios)">
</body>
</html>

现在,在几乎所有情况下这都很漂亮,我看到ios is: object UIWebView和Xcode控制台中的对象。 很酷。 但是在一个特定的场景中,100%的时间,在UIWebView中的一定数量的重定向后失败,并且一旦上面的页面最终加载它,它说:

ios是:undefined

...以及JS逻辑的其余部分退出,因为在setContent函数中对ios.jsLog的后续调用会导致未定义的对象异常。

最后我的问题是: 什么可能/可能导致JSContext丢失 我穿过JavaScriptCore的.h文件的“文件”挖,发现这是应该发生的唯一途径是,如果没有更strong的引用JSContext ,但对我来说,我有我自己的一个,所以没有按好像没错。

我唯一的另一个假设是它与我获取JSContext引用的方式有关:

 self.js = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

我知道这可能不会得到Apple的正式支持,尽管我确实发现至少有一个SO'er表示他有一个苹果认可的应用程序使用了这种方法。

编辑

我应该提一下,我实现了UIWebViewDelegate以便在UIWebView中的每次重定向之后检查JSContext:

-(void)webViewDidFinishLoad:(UIWebView *)view{
    // Write to Xcode console via our JSContent - is it still valid?
    [self.js evaluateScript:@"ios.jsLog('Can see JS from obj c');"];
}

这适用于所有情况,即使我的网页最终加载和报告ios is: undefined上述方法同时写入Can see JS from obj c到Xcode控制台。 这似乎表明JSContext仍然有效,并且由于某种原因,它在JS中根本不再可见。


对这个非常冗长的问题道歉,关于这方面的文档很少,我认为我能提供的信息越多越好。

页面加载可能导致WebView(以及包装WebView的UIWebView)获取新的JSContext。

如果这是我们讨论的MacOS,那么就像2013年WWDC中关于WebView的部分所示,在Apple的开发者网络上将“将JavaScript集成到本机应用程序”会话中显示( https://developer.apple.com/videos/wwdc/2013 /?id = 615 ),您需要为框架加载实现委托,并在webView的选择器实现中初始化JSContext变量:didCreateJavaScriptContext:forFrame:

对于IOS,您需要在webViewDidFinishLoad中执行此操作:

-(void)webViewDidFinishLoad:(UIWebView *)view{
    self.js = [view valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // Undocumented access to UIWebView's JSContext
    self.js[@"ios"] = self;
}

之前的JSContext仍然可用于Objective-C,因为您已经保留了对它的强引用。

检查这个UIWebView JSContext

关键点是JSContext更改后注册一个javascript对象。 我使用runloop观察器检查是否有任何网络操作完成,一旦完成,我将获得更改的JSContext,并注册我想要的任何对象。

我没有尝试if if的这个工作,如果你必须在iframe中注册一些对象,试试这个

NSArray *frames = [_web valueForKeyPath:@"documentView.webView.mainFrame.childFrames"];

[frames enumerateObjectsUsingBlock:^(id frame, NSUInteger idx, BOOL *stop) {
    JSContext *context = [frame valueForKeyPath:@"javaScriptContext"];
    context[@"Window"][@"prototype"][@"alert"] = ^(NSString *message) {
        NSLog(@"%@", message);
    };
}];    

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM