【问题标题】:Where to place a completionHandler when inside loops?在循环内放置completionHandler 的位置?
【发布时间】:2016-12-15 19:25:45
【问题描述】:

我在这个函数中使用了completionHandler,但是它嵌套在几个for循环中(如下)。问题是每次循环运行时都会调用它的处理程序,而我只希望处理程序在整个函数完成处理时传入Set。如果我将它放在循环之外,那么它会被过早地调用并且是空的。我应该在这里做什么?

现在,当我打印到控制台进行测试时,它会打印: 设置项目 1 设置项目 1、2 设置项目1、2、3等。

struct RekoRequest {




    public func getRekos(rekoType: rekoCategory, handler: @escaping (Set<String>) -> Void) {

        var urls = [NSURL]()
        var IDs = Set<String>()


        TwitterRequest().fetchTweets(searchType: "things") { result in


            guard let tweets = result as? [TWTRTweet] else {print("Error in getRekos receiving tweet results from TwitterRequest.fetchTweets"); return}

            for tweet in tweets {





                let types: NSTextCheckingResult.CheckingType = .link
                let detector = try? NSDataDetector(types: types.rawValue)
                guard let detect = detector else { print("NSDataDetector error"); return }

                let matches = detect.matches(in: text, options: .reportCompletion, range: NSMakeRange(0, (text.characters.count)))



                for match in matches {


                    if let url = match.url {

                        guard let unwrappedNSURL = NSURL(string: url.absoluteString) else {print("error converting url to NSURL");return}

                        //Show the original URL
                        unwrappedNSURL.resolveWithCompletionHandler {

                            guard let expandedURL = URL(string: "\($0)") else {print("couldn't covert to expandedURL"); return}


                            guard let urlDomain = expandedURL.host else { print("no host on expandedURL"); return }

                            switch urlDomain {


                            case "www.somesite.com":

                                let components = expandedURL.pathComponents

                                for component in components {
                                    if component == "dp" {
                                        guard let componentIndex = components.index(of: component) else {print("component index error"); return}
                                        let IDIndex = componentIndex + 1
                                        let ID = components[IDIndex]

                                        //Filter out Dups and add to Set
                                        IDs.insert(ID)


                                        handler(IDs) 

                                        print(ID) //this prints multiple sets of IDs, I only want one when the function is finished completely

                                    }
                                }

                                break;

                            default:
                                break;
                            }
                        }


                    } else { print("error with match.url") }

                } //for match in matches loop 




            } //for tweet in tweets loop


        }
    }







}


// Create an extension to NSURL that will resolve a shortened URL
extension NSURL
{
    func resolveWithCompletionHandler(completion: @escaping (NSURL) -> Void)
    {
        let originalURL = self
        let req = NSMutableURLRequest(url: originalURL as URL)
        req.httpMethod = "HEAD"

        URLSession.shared.dataTask(with: req as URLRequest)
        {
            body, response, error in completion(response?.url as NSURL? ?? originalURL)
            }
            .resume()
    }
}

【问题讨论】:

  • 为什么不将完成处理程序放在循环之后?
  • 当我把它放在循环之后,我得到一个空集
  • @GarySabo 你在哪里插入IDs 集合? setOfIDs 应该是IDs 吗?
  • 抱歉,我稍微清理了一下代码,错过了,它是 for 组件循环中的 IDs.insert(ID)

标签: ios swift for-loop completionhandler


【解决方案1】:

在 for 循环之后调用您的完成处理程序。

for component in components {
    if component == "dp" {
        ...
    }
}
handler(IDs)

重要提示:handler 应在 for 循环之外 调用,但在 TwitterRequest().fetchTweets() 尾随闭包调用。 p>


处理空集的方法

您的IDs 正在初始化为一个空集。只有在您的 for 循环中满足某些条件后,才会将值插入到该集合中。如果不满足这些条件,那么您的 IDs 集将为空。

如果这是不可取的,那么您将不得不更改您的完成处理程序或更改您的条件逻辑,以便您始终获得一个非空集。

一种方法可能是在您的回调中设置可选值。比如:

(Set&lt;String&gt;?) -&gt; Void

如果IDs 为空,则使用nil 回调并让您的调用代码处理nil 设置的可能性。

另一种方法可能是创建一个枚举来封装您的结果并在您的回调中使用它。比如:

枚举

enum Result {
     case success(Set<String>)
     case failure
}

回调

handler: (Result) -&gt; Void

用法

handler(.success(IDs))

// or 

handler(.failure)

调用代码

getReckos(rekoType: .someType) { result in
    switch result {
    case .success(let IDs):
        // Use IDs
    case .failure:
       // Handle no IDs
    }
}

【讨论】:

  • 谢谢,我可能没有正确地表达它,但问题是我希望完成处理程序在循环遍历并完成时将其结果发送到它的调用函数RekoRequest().getRekos(rekoType: sometype) { result in,所以我只得到 1 个完整的 Set....截至目前,我每次通过循环都得到一个 Set,即 1、1 和 2、1 2 和 3 等。
  • @GarySabo handler 需要在 for 循环之外调用。这样,当循环完成时,您的 handler 将使用 IDs 调用一次。
  • @GarySabo 我的回答中的其他要点是试图解决您对空集的评论。您当前编写代码的方式可以使用空集。我的回答提供了一些关于如何处理潜在空集的建议。
  • 谢谢,我在我的问题中添加了我称之为unwrappedNSURL.resolveWithCompletionHandler { 的自定义扩展代码,该代码从缩短的 URL 中获取了完整的 URL ....我忘记了是异步的,我认为这可能是问题我收到多个处理程序调用?
  • 也许没关系,最后我通过了完整的集合..所以我想我当前代码的唯一缺点是它不如处理程序的性能好仅在循环完成后通过
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-25
  • 2017-02-08
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
相关资源
最近更新 更多