在返回值Swift的函数中执行多线程的最佳实践



我有一个问题,可能不是关于实现的,而是关于技巧/最佳实践的问题。

我正在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
        }
    }

最新更新