分派到主线程时返回值

时间:2018-08-18 14:09:05

标签: ios swift multithreading grand-central-dispatch

我有一个从后台线程调用的函数func getValue() -> Bool。这是有意的,也是必需的。现在,getValue()需要在主线程上执行某些操作,在这种情况下,它需要访问UIApplication.shared.canOpenURL,该访问必须在主队列上运行。

这是我当前的功能:

func getValue() -> Bool {
    guard let url = URL(string: "someurl") else { return false }
    return UIApplication.shared.canOpenURL(url)
}

如何将该函数转换为线程安全的函数,即确保该函数始终在主线程上运行,而无需

  • 从主线程开始调用函数
  • 重构函数以在闭包中返回值

我已经尝试过了:

// This causes a deadlock, see https://stackoverflow.com/a/42484670/1531270
func getValue() -> Bool {
    var flag = false

    let group = DispatchGroup()
    group.enter()
    DispatchQueue.main.async {
        if let url = URL(string: "someurl"), UIApplication.shared.canOpenURL(url) {
            flag = true
        }
        group.leave()
    }
    group.wait()

    return flag
}

这:

// This crashes with EXC_BREAKPOINT (SIGTRAP) dispatch_sync called on queue already owned by current thread
func getValue() -> Bool {
    return DispatchQueue.main.sync {
        guard let url = URL(string: "someurl") else { return false }
        return UIApplication.shared.canOpenURL(url)
    }
}

但它们都不起作用。有什么想法吗?

2 个答案:

答案 0 :(得分:3)

您正在寻找信号灯-试试这个:

DispatchQueue.global(qos: .background).async {
    var value: Bool? = nil
    let semaphore = DispatchSemaphore(value: 0)
    DispatchQueue.main.async {
        let alert = UIAlertController(title: "Choose one", message: "Take your time, I'll wait", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "true", style: .default, handler: { _ in
            value = true
            semaphore.signal()
        }))
        alert.addAction(UIAlertAction(title: "false", style: .default, handler: { _ in
            value = false
            semaphore.signal()
        }))
        self.present(alert, animated: true, completion: nil)
    }
    semaphore.wait()
    print("Choice: \(value!)")
}

或者使用上面的示例:

func getValue() -> Bool {
    var flag = false
    let semaphore = DispatchSemaphore(value: 0)

    DispatchQueue.main.async {
        if let url = URL(string: "someurl"), UIApplication.shared.canOpenURL(url) {
            flag = true
            semaphore.signal()
        }
    }
    semaphore.wait()
    return flag
}

答案 1 :(得分:1)

对于您的第二个示例,我无法重现任何问题。您没有显示自己的通话方式getValue,所以我做了一些事情:

func getValue() -> Bool {
   return DispatchQueue.main.sync {
        guard let url = URL(string: "testing://testing") else { return false }
        return UIApplication.shared.canOpenURL(url)
   }
}
override func viewDidLoad() {
    super.viewDidLoad()
    DispatchQueue.global(qos:.background).async {
        let ok = self.getValue()
        print(ok) // false, the right answer
    }
}

没有“崩溃”,所以我就去解决。当我使用方案testing:时,我得到false,而当我将testing:更改为https:时,我返回了true,因此该方法调用显然是有效的。