在内部循环时在哪里放置 completionHandler?
Where to place a completionHandler when inside loops?
我在此函数中使用了 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: "\([=10=])") 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()
}
}
在 for 循环之后调用完成处理程序。
for component in components {
if component == "dp" {
...
}
}
handler(IDs)
Important: The handler
should be called outside of the for loop, but within the TwitterRequest().fetchTweets()
trailing closure.
处理空集的方法
您的 IDs
正在初始化为空集。只有在 for 循环中满足特定条件后,才会将值插入到该集合中。如果不满足这些条件,那么您的 IDs
集合将为空。
如果这是不可取的,那么您将不得不更改完成处理程序或更改条件逻辑,以便始终获得非空集。
一种方法可能是在回调中设置可选。类似于:
(Set<String>?) -> Void
如果 IDs
为空,则使用 nil
回调并让您的调用代码处理 nil
集合的可能性。
另一种方法可能是创建一个枚举来封装您的结果并在您的回调中使用它。类似于:
枚举
enum Result {
case success(Set<String>)
case failure
}
回调
handler: (Result) -> 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
}
}
我在此函数中使用了 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: "\([=10=])") 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()
}
}
在 for 循环之后调用完成处理程序。
for component in components {
if component == "dp" {
...
}
}
handler(IDs)
Important: The
handler
should be called outside of the for loop, but within theTwitterRequest().fetchTweets()
trailing closure.
处理空集的方法
您的 IDs
正在初始化为空集。只有在 for 循环中满足特定条件后,才会将值插入到该集合中。如果不满足这些条件,那么您的 IDs
集合将为空。
如果这是不可取的,那么您将不得不更改完成处理程序或更改条件逻辑,以便始终获得非空集。
一种方法可能是在回调中设置可选。类似于:
(Set<String>?) -> Void
如果 IDs
为空,则使用 nil
回调并让您的调用代码处理 nil
集合的可能性。
另一种方法可能是创建一个枚举来封装您的结果并在您的回调中使用它。类似于:
枚举
enum Result {
case success(Set<String>)
case failure
}
回调
handler: (Result) -> 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
}
}