UIWebView JavaScript失去对iOS JSContext名称空间(对象)的引用 [英] UIWebView JavaScript losing reference to iOS JSContext namespace (object)

查看:102
本文介绍了UIWebView JavaScript失去对iOS JSContext名称空间(对象)的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been working on a proof of concept app that leverages two-way communication between Objective C (iOS 7) and JavaScript using the WebKit JavaScriptCore framework. I was finally able to get it working as expected, but have run into a situation where the UIWebView loses its reference to the iOS object that I've created via JSContext.

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

The app is a bit complex, here are the basics:


  • 我正在iOS设备上运行Web服务器(CocoaHTTPServer)

  • UIWebView最初加载一个远程URL,稍后作为应用程序流程的一部分重新定向回 localhost (想想OAuth)

  • 应用程序托管的HTML页面(在localhost上)具有应与我的iOS代码对话的JavaScript

  • I'm running a web server on the iOS device (CocoaHTTPServer)
  • The UIWebView initially loads a remote URL, and is later redirected back to localhost as part of the app flow (think OAuth)
  • The HTML page that the app hosts (at localhost) has the JavaScript that should be talking to my iOS code

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

Here's the iOS side, my ViewController's .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

And the pertinent parts of the ViewController's .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方面:

Here is the (simplified for the sake of this question) HTML/JS side:

<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是:对象在UIWebView和Xcode的控制台中。很酷。但在一个特定场景中,100%的时间,在UIWebView中的一定数量的重定向后失败,并且一旦上面的页面最终加载它就说:

Now, in almost all cases this works beautifully, I see ios is: object both in the UIWebView and in Xcode's console. Very cool. But in one particular scenario, 100% of the time, this fails after a certain number of redirects in the UIWebView, and once the above page finally loads it says:



ios是:undefined

ios is: undefined


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

...and the rest of the JS logic quits because the subsequent call to ios.jsLog in the setContent function results in an undefined object exception.

最后我的问题是:什么可能/可能导致JSContext丢失?我在JavaScriptCore的.h文件中挖掘了文档,发现这应该发生的唯一方法是,如果没有更多的 strong 引用 JSContext ,但在我的情况下,我有一个我自己的,所以这似乎不对。

So finally my question: what could/can cause a JSContext to be lost? I dug through the "documentation" in the JavaScriptCore's .h files and found that the only way this is supposed to happen is if there are no more strong references to the JSContext, but in my case I have one of my own, so that doesn't seem right.

我唯一的其他假设是因为它与我获取 JSContext 参考的方式有关:

My only other hypothesis is that it has to do with the way in which I'm acquiring the JSContext reference:

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

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

I'm aware that this may not be officially supported by Apple, although I did find at least one SO'er that said he had an Apple-approved app that used that very method.

EDIT

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

I should mention, I implemented UIWebViewDelegate to check the JSContext after each redirect in the UIWebView thusly:

-(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是:undefined 上面的方法同时写可以从obj c 看到JS到Xcode控制台。这似乎表明JSContext仍然有效,并且由于某种原因,它从JS中不再可见。

This works in all cases, even when my web page finally loads and reports ios is: undefined the above method simultaneously writes Can see JS from obj c to the Xcode console. This would seem to indicate the JSContext is still valid, and that for some reason it's simply no longer visible from JS.

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

Apologies for the very long-winded question, there is so little documentation on this out there that I figured the more info I could provide, the better.

推荐答案

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

The page load can cause the WebView (and UIWebView which wraps WebView) to get a new JSContext.

如果这是MacOS我们在谈论,然后如Apple WWD开发人员网络上的将JavaScript集成到本机应用程序会话中的WebView部分所示( https://developer.apple.com/videos/wwdc/2013/?id=615 ),你需要为框架加载实现一个委托,并在你的实现中初始化你的JSContext变量 webView的选择器:didCreateJavaScriptContext:forFrame:

If this was MacOS we were talking about, then as shown in the section on WebView in the 2013 WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network (https://developer.apple.com/videos/wwdc/2013/?id=615), you would need to implement a delegate for the frame load and initialise your JSContext variables in your implementation of the selector for webView:didCreateJavaScriptContext:forFrame:

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

In the case of IOS, you need to do this in 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,因为你保留了一个强烈提及它。

The previous JSContext is still available to Objective-C since you've kept a strong reference to it.

这篇关于UIWebView JavaScript失去对iOS JSContext名称空间(对象)的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆