从 JavaScript 调用 swift 方法挂起 xcode 并且应用程序

Call to swift method from JavaScript hangs xcode and application

我正在使用 JavascriptCode 框架编写一个 iOS 应用程序(使用 xcode 7.3 和 swift 2.2)。从 swift 调用 javascript 方法是完美的,但是当我从 javascript 调用 swift 方法时,xcode 只显示一个 "loading" 类型的符号没有任何反应。我需要 "force quit" xcode 才能摆脱这种状态。 我已经关注 https://www.raywenderlich.com/124075/javascriptcore-tutorial and http://nshipster.com/javascriptcore/ 并且正在尝试非常简单的调用。

有人遇到过这种问题吗?

我的swift代码如下:

@objc protocol WindowJSExports : JSExport {
   var name: String { get set }
   func getName() -> String
   static func createWindowWithName(name: String) -> WindowJS
}

@objc class WindowJS : NSObject, WindowJSExports {
   dynamic var name: String
   init(name: String) {
       self.name = name
   }    
   class func createWindowWithName(name: String) -> WindowJS {
       return WindowJS(name: name)
   }    
   func getName() -> String {
       NSLog("getName called from JS context")
       return "\(name)"
   }
} 

我正在初始化上下文如下:

runContext = JSContext()
runContext.name = "test_Context"

windowToJs = WindowJS(name: "test")
runContext.setObject(windowToJs.self, forKeyedSubscript: "WindowJS")

如果我在没有实例化的情况下将上面代码中的最后两行替换为下面的代码,代码将无法加载。

runContext.setObject(WindowJS.self, forKeyedSubscript: "WindowJS")

而javascript代码就像

一样简单
function check() {
    return WindowJS.getName()
}

我确实看到在 JS 函数检查中遇到了断点,当 WindowJS.getName 被调用时,xcode 变得没有响应。

您正在创建一个死锁,因为您正在从 Swift 调用到 JavaScript 返回到 Swift。我不确定为什么会出现死锁,但我最近在 Mac 上遇到了与 WKWebView 类似的问题。

您需要将其解耦并使通信异步。这显然意味着在这种情况下,您不能简单地 return 来自 JS 函数的值。

要解耦,您可以通过使用 setTimeout:

推迟 JavaScript 函数需要在当前运行循环迭代之外完成的工作来打破僵局
function myFunction() {
  setTimeout(function() {
    // The actual work is done here.
    // Call the Swift part here.
  }, 0);
}

整个原生↔︎JavaScript沟通非常非常棘手。如果可以,请避免使用它。有一个名为 XWebView 的项目可能会帮助您,因为它试图简化两个世界之间的桥梁。

可以通过将以下代码添加到我的 swift 函数来解决 setTimeout。

let setTimeout: @convention(block) (JSValue, Int) -> () = 
{ callback, timeout in
    let timeVal = Int64(timeout)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeVal), dispatch_get_main_queue(), { callback.callWithArguments(nil)})
}

为了将本机代码暴露给 JS 上下文,我还添加了以下内容。

runContext.setObject(unsafeBitCast(setTimeout, AnyObject.self), forKeyedSubscript: "setTimeout")

然后一切正常。