簡體   English   中英

在嵌套完成處理程序之后執行代碼

[英]Executing code after nested completion handlers

我正在編寫一個 Safari 應用程序擴展,並希望在我的視圖控制器中獲取活動頁面的 URL。

這意味着嵌套完成處理程序來獲取窗口、獲取選項卡、獲取頁面、訪問其屬性。 煩人但足夠簡單。 它看起來像這樣:

func doStuffWithURL() {

    var url: URL?

    SFSafariApplication.getActiveWindow { (window) in
        window?.getActiveTab { (tab) in
            tab?.getActivePage { (page) in
                page?.getPropertiesWithCompletionHandler { (properties) in
                    url = properties?.url
                }
            }
        }
    }

    // NOW DO STUFF WITH THE URL
    NSLog("The URL is \(String(describing: url))")

}

顯而易見的問題是它不起作用。 作為完成處理程序,它們直到函數結束才會被執行。 變量url將為 nil,並且這些東西將在任何嘗試獲取 URL 之前完成。

解決此問題的一種方法是使用DispatchQueue 它有效,但代碼真的很丑:

func doStuffWithURL() {

    var url: URL?

    let group = DispatchGroup()
    group.enter()
    SFSafariApplication.getActiveWindow { (window) in
        if let window = window {
            group.enter()
            window.getActiveTab { (tab) in
                if let tab = tab {
                    group.enter()
                    tab.getActivePage { (page) in
                        if let page = page {
                            group.enter()
                            page.getPropertiesWithCompletionHandler { (properties) in
                                url = properties?.url
                                group.leave()
                            }
                        }
                        group.leave()
                    }
                }
                group.leave()
            }
        }
        group.leave()
    }

    // NOW DO STUFF WITH THE URL
    group.notify(queue: .main) {
        NSLog("The URL is \(String(describing: url))")
    }

}

if塊需要知道我們沒有處理一個 nil 值。 我們需要一定的完成處理程序將返回,並因此.leave()調用之前,我們可以調用.enter()到后端向上為零。

我什至無法在某種getURLForPage()函數或擴展中getURLForPage()所有這些丑陋之處(添加某種SFSafariApplication.getPageProperties將是我的偏好),因為顯然您無法從.notify塊中的函數返回。

盡管我嘗試使用queue.wait和不同的DispatchQueue創建一個函數,如下面的答案中所述,以便能夠使用 return...

https://stackoverflow.com/a/42484670/2081620

......對我來說這並不奇怪,它會導致死鎖,因為.wait仍在主隊列上執行。

有沒有更好的方法來實現這一目標? 順便說一句,“要做的事情”是根據用戶請求更新 UI,因此需要在主隊列中。

編輯:為免生疑問,這不是 iOS 問題。 雖然類似的原則適用,但 Safari 應用程序擴展是僅適用於 macOS 的 Safari 的一項功能。

感謝Larme在評論中的建議,我想出了一個隱藏丑陋,可重用,並保持代碼干凈和標准的解決方案。

嵌套的完成處理程序可以由SFSafariApplication類的擴展替換,這樣代碼主體中只需要一個。

extension SFSafariApplication {

    static func getActivePageProperties(_ completionHandler: @escaping (SFSafariPageProperties?) -> Void) {

        self.getActiveWindow { (window) in
            guard let window = window else { return completionHandler(nil) }
            window.getActiveTab { (tab) in
                guard let tab = tab else { return completionHandler(nil) }
                tab.getActivePage { (page) in
                    guard let page = page else { return completionHandler(nil) }
                    page.getPropertiesWithCompletionHandler { (properties) in
                        return completionHandler(properties)
                    }
                }
            }
         }

    }

}

然后在代碼中它可以用作:

func doStuffWithURL() {

    SFSafariApplication.getActivePageProperties { (properties) in
        if let url = properties?.url {
            // NOW DO STUFF WITH THE URL
            NSLog("URL is \(url))")
        } else {
            // NOW DO STUFF WHERE THERE IS NO URL
            NSLog("URL ERROR")
        }
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM