Multithreading 在返回值Swift的函数中进行多线程处理的最佳实践
我有一个问题可能不是关于实现的,而是关于技巧/最佳实践的问题 我正在使用Swift开发一个类,该类以JSON格式从在线源获取数据。我想在这个类中有一些特定的方法,这些方法连接到在线源代码,并在字典类型中返回结果。函数可能如下所示:Multithreading 在返回值Swift的函数中进行多线程处理的最佳实践,multithreading,swift,design-patterns,architectural-patterns,Multithreading,Swift,Design Patterns,Architectural Patterns,我有一个问题可能不是关于实现的,而是关于技巧/最佳实践的问题 我正在使用Swift开发一个类,该类以JSON格式从在线源获取数据。我想在这个类中有一些特定的方法,这些方法连接到在线源代码,并在字典类型中返回结果。函数可能如下所示: func getListFromOnline()->[String:String]{ var resultList : [String:String]=[:] ... /* Some HTTP request is sent t
func getListFromOnline()->[String:String]{
var resultList : [String:String]=[:]
...
/*
Some HTTP request is sent to the online source with NSURLRequest
Then I parse the result and assign it to the resultList
*/
...
return resultList
}
func asynchStuff(completionHandler: ([String:String]) -> Void) {
// do asynchronous stuff, building a [String:String]
let result: [String: String] = // the result we got async
completionHandler(result)
}
我现在在这个实现中得到的是,在没有多线程的情况下,resultList
显然是在从在线源获取之前返回的。毫不奇怪,它会导致程序失败
有什么想法或技巧可以让我达到相反的效果吗?在这个例子中,我对多线程有点困惑,因为我想稍后从另一个类异步调用这个公共方法,我知道如何做,但我不知道如何使返回参数的方法本身具有多线程
或者它根本不是关于多线程的,而我在这里没有看到一个明显的解决方案?简短的回答:你不能。这不是异步处理的工作方式。在方法返回时,结果将永远不可用。事实上,您的方法在异步处理之前返回 正如ABakerSmith在他的评论中所说,您应该做的是添加一个完成处理程序闭包(aka block)作为函数的参数。这是调用者传入的代码块。编写方法是为了在异步JSON数据下载完成后调用完成块 然后在调用者中编写代码,以便在完成块中下载完成后,传入要执行的代码。您必须对程序进行结构化,使其能够在没有数据的情况下运行(例如,通过显示空的表视图),并且在数据到达后更新本身。您可以编写完成块,在表视图的数据模型中安装数据,然后在表视图上调用reloadData
如果编写异步方法,使其在主线程上执行完成块,通常会更安全。在Swift/Objective-C开发中,有三种简单且完全预期的方法可以解决此问题,并且这两种解决方案都不涉及直接返回值的方法。您可以编写等待异步部分完成(阻塞线程)的代码,然后返回值,在某些情况下,这是在苹果自己的一些库中完成的,但我不打算介绍这种方法,因为这真的不是一个好主意
第一种方法涉及完成区块 当我们的方法要执行一些异步代码时,我们可以在异步工作完成时传入一个要执行的代码块。这种方法如下所示:
func getListFromOnline()->[String:String]{
var resultList : [String:String]=[:]
...
/*
Some HTTP request is sent to the online source with NSURLRequest
Then I parse the result and assign it to the resultList
*/
...
return resultList
}
func asynchStuff(completionHandler: ([String:String]) -> Void) {
// do asynchronous stuff, building a [String:String]
let result: [String: String] = // the result we got async
completionHandler(result)
}
记住在调用asynchStuff
的同一线程上调用completionHandler()
。(本例并未说明这一点。)
第二种方法涉及代表和协议 我们需要一个类来完成异步工作。此类将保存对委托的引用,委托将实现完成方法。第一,协议:
@objc protocol AsyncDelegate {
func complete(result: [String:String]
}
现在,我们的异步工作者:
class AsyncWorker {
weak var delegate: AsyncDelegate?
func doAsyncWork() {
// like before, do async work...
let result: [String: String] = // the result we got async
self.delegate?.complete(result)
}
}
请记住,我们在调用doAsyncWork()
的同一线程上调用完成委托方法。(本例并未说明这一点。)
第三种方法可以使用
NSNotificationCenter
完成。这是一种合适的方法,这种情况非常罕见,我甚至不会像其他两个例子那样为一个基本的例子操心,因为几乎可以肯定,在几乎所有的场景中,你都应该使用前两个例子中的一个
您使用哪种方法完全取决于您的特定用例。在Objective-C中,我经常倾向于委托而不是基于块的方法(尽管有时块是正确的),但Swift允许我们将常规函数/方法作为块参数传递,因此它使我稍微倾向于使用基于块的方法进行Swift,但仍然一如既往,使用正确的工具进行正确的工作
我想扩展这个答案,以解决在适当的线程上调用回调(无论是块还是委托)的问题。我不确定是否还有办法用GCD实现这一点,但我们可以用
NSOperationQueue
s实现
func asyncStuff(completionHandler: ([String:String]) -> Void) {
let currentQueue = NSOperationQueue.currentQueue()
someObject.doItsAsyncStuff(someArg, completion: {
result: [String:String] in
currentQueue.addOperationWithBlock() {
completionHandler(result)
}
}
}
下面是一个示例,说明如何在单独的线程中执行异步工作,然后返回主线程以更新UI
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some asynchronous work
dispatch_async(dispatch_get_main_queue()) {
// use returned data to update some UI on the main thread
}
}
您可以使用一个完成处理程序,一旦得到结果就会调用它。否。应该在调用方法的线程上执行完成块。@nhgrif和Duncan:我不同意:应该在调用站点无法访问的私有“执行上下文”(线程、GCD队列、NSOperationQueue等)或“执行上下文”上调用完成块调用站点已显式指定的-如果有参数的话。这通常可以避免潜在的死锁和性能问题。在Mac OS/iOS上无法使用调用站点当前的执行上下文,因为没有隐式给出的“当前执行上下文”的抽象定义。这取决于您使用的是GCD还是NSO操作。我现在不能更新答案(在电话上),但我可以稍后再更新@Gozonery您仍然可以使用NSOperationQueues。。。当我有机会做一点自己的研究时,我将以任何方式更新这个答案。@GoZoner GCD
dispatch\u get\u current\u queue
已被弃用,但NSOperationQueue
的currentQueue
未被弃用。已经说过,<代码> NSURLSECTION/CODE>和AfNETWEB完全避免了这个问题(A)