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

lak*_*ffm 2 multithreading design-patterns architectural-patterns 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
}
Run Code Online (Sandbox Code Playgroud)

我在这个实现中得到的,没有多线程,resultList显然是从在线源获取之前返回的.毫不奇怪,它会导致程序失败.

任何想法或提示如何实现相反的目标?我在这个实例中对多线程有点困惑,因为我想稍后从另一个类异步调用这个公共方法,我知道如何做到这一点,但是我不知道如何让返回参数的方法在多个内部进行多线程处理本身.

或者根本不是多线程,我在这里没有看到明显的解决方案?

nhg*_*rif 7

在Swift/Objective-C开发中有三种简单且完全期望的方法可以解决这个问题,这些解决方案都不涉及直接返回值的方法.你可以编写等待异步部分完成的代码(阻塞一个线程),然后返回值,有些情况下这是在一些Apple自己的库中完成的,但是我不打算介绍这种方法,因为这真的不是一个好主意.


第一种方法涉及完成块.

当我们的方法要执行一些异步代码时,我们可以传入一段代码,以便在异步工作完成时执行.这样的方法看起来像这样:

func asynchStuff(completionHandler: ([String:String]) -> Void) {
   // do asynchronous stuff, building a [String:String]
   let result: [String: String] = // the result we got async
   completionHandler(result)
}
Run Code Online (Sandbox Code Playgroud)

记得调用被调用completionHandler()的同一个线程asynchStuff.(这个例子没有证明这一点.)


第二种方法涉及代表和协议.

我们需要一个类来完成异步工作.该类将包含对我们的委托的引用,该委托将实现完成方法.一,协议:

@objc protocol AsyncDelegate {
    func complete(result: [String:String]
}
Run Code Online (Sandbox Code Playgroud)

现在我们的异步工作者:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

请记住确保我们在调用的同一个线程上调用完成委托方法doAsyncWork().(这个例子没有证明这一点.)


第三种方法可以使用NSNotificationCenter.这是适当的方法的时间将是非常罕见的,甚至我甚至不打扰一个基本的例子,就像我做其他两个例子,因为你几乎肯定会使用前两个例子之一每个场景.


您使用哪种方法完全取决于您的具体用例.在Objective-C中,我经常更喜欢委托基于块的方法(尽管有时块是正确的),但Swift允许我们将常规函数/方法作为块参数传递,因此它稍微倾向于使用基于块的Swift方法但是,仍然一如既往地使用正确的工具来完成正确的工作.


我想扩展这个答案来解决在适当的线程上调用回调(无论是块还是代理)的问题.我不确定是否有办法用GCD做到这一点,但我们可以用NSOperationQueues 做.

func asyncStuff(completionHandler: ([String:String]) -> Void) {
    let currentQueue = NSOperationQueue.currentQueue()
    someObject.doItsAsyncStuff(someArg, completion: { 
        result: [String:String] in
        currentQueue.addOperationWithBlock() {
            completionHandler(result)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)