我有一个问题,可能不是关于实现的,而是关于技巧/最佳实践的问题。
我正在Swift中开发一个类,该类从JSON格式的在线源获取数据。我希望在这个类中有特定的方法,连接到在线源,并以Dictionary类型返回结果。功能可能是这样的:
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
}
我现在在这个实现中得到的是,在没有多线程的情况下,resultList
显然在从在线源进行获取之前返回。毫不奇怪,它会导致程序失败。
有什么想法或技巧可以让我达到相反的效果吗?在这个例子中,我对多线程有点困惑,因为我想稍后从另一个类异步调用这个公共方法,我知道如何做到这一点,但我不知道如何使返回参数的方法本身具有多线程。
或者它根本不是关于多线程的,我在这里没有看到明显的解决方案?
Swift/Objective-C开发中有三种简单且完全令人期待的方法来解决这个问题,但这两种解决方案都不涉及直接返回值的方法。可以编写等待异步部分完成(阻塞线程)的代码,然后返回值,在某些情况下,这是在苹果自己的一些库中完成的,但我不打算介绍这种方法,因为这真的不是一个好主意。
第一种方法涉及完成块。
当我们的方法要执行一些异步代码时,我们可以传入一块代码,以便在异步工作完成时执行。这样的方法看起来是这样的:
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()
的同一线程上调用完成委托方法
第三种方法可以使用CCD_ 5来完成。这是一种合适的方法的情况非常罕见,以至于我甚至不会像其他两个例子那样去处理一个基本的例子,因为你几乎肯定应该在每个场景中使用前两个例子中的一个。
使用哪种方法完全取决于您的具体用例。在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)
}
}
}
简短回答:你不能。异步处理不是这样工作的。当您的方法返回时,结果将永远不可用。事实上,您的方法在异步处理出现之前就返回了。
正如ABakerSmith在他的评论中所说,您应该做的是添加一个完成处理程序闭包(又名块)作为函数的参数。这是调用方传入的代码块。您编写方法,以便在异步JSON数据下载完成后,它调用完成块。
然后在调用者中编写代码,以便在下载完成后传入要执行的代码。你必须构建你的程序,这样它就可以在没有数据的情况下运行(例如,通过显示一个空表视图),并且在数据到达后更新自己。您可以编写完成块,将数据安装在表视图的数据模型中,然后在表视图上调用reloadData。
如果编写异步方法,使其在主线程上执行完成块,通常会更安全。
这里是一个如何在单独的线程中进行异步工作,然后返回到主线程以更新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
}
}