3

我有一个问题可能不是专门关于实施的,而是一个提示/最佳实践的问题。

我正在用 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显然是在从在线源获取完成之前返回的。毫不奇怪,它会导致程序失败。

有什么想法或提示我可以实现相反的效果吗?在这种情况下,我对多线程有点困惑,因为我想稍后从另一个类异步调用这个公共方法,我知道如何做到这一点,但我不知道如何使返回参数的方法具有多线程本身。

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

4

3 回答 3

8

在 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)
}

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


第二种方法涉及委托和协议。

我们需要一个类来完成异步工作。此类将持有对我们的委托的引用,该委托将实现完成方法。首先,一个协议:

@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 做到这一点,但我们可以用NSOperationQueues.

func asyncStuff(completionHandler: ([String:String]) -> Void) {
    let currentQueue = NSOperationQueue.currentQueue()
    someObject.doItsAsyncStuff(someArg, completion: { 
        result: [String:String] in
        currentQueue.addOperationWithBlock() {
            completionHandler(result)
        }
    }
}
于 2015-06-27T21:51:22.630 回答
1

简短的回答:你不能。这不是异步处理的工作方式。在您的方法返回时,结果将永远不可用。实际上,您的方法甚至在异步处理之前就返回了。

正如 ABakerSmith 在他的评论中所说,您应该做的是添加一个完成处理程序闭包(又名块)作为函数的参数。那是调用者传入的代码块。您编写方法,以便在完成异步 JSON 数据下载后,它会调用完成块。

然后在您的调用者中编写代码以在下载完成后在 COMPLETION BLOCK中传递您想要执行的代码。您必须构建您的程序,以便它可以在没有您的数据的情况下运行(例如,通过显示一个空的表格视图)并且在数据到达后自行更新。您可以编写完成块以将数据安装到表视图的数据模型中,然后在表视图上调用 reloadData。

如果您编写异步方法通常会更安全,以便它在主线程上执行完成块。

于 2015-06-27T21:09:16.427 回答
0

这是一个如何在单独的线程中执行异步工作然后返回主线程以更新 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
        }
    }
于 2015-06-28T01:23:02.620 回答