使用 Grand Central Dispatch 的问题 IOS Swift Xcode 6.3.1

problems using Grand Central Dispatch IOS Swift Xcode 6.3.1

我正在学习 swift,但很难理解多线程问题..

我遇到的具体问题是我正在从互联网加载数据并且 在使用 dispatch_async 时尝试 return 包含此数据的数组 ("broadcasts")。

我的问题是空数组的 return 执行发生在 数组中填满了数据(这一行 "println(broadcasts)" 发生但数组 return 为空..)

这是我的代码:

import UIKit

public class BroadcastRequest {

    func requestNewBroadcasts() -> [BroadcastModel] {
        var broadcasts = [BroadcastModel]()
        var sectionsOfBroadcasts = [[BroadcastModel]]()
        DataManager.getBroadcastsFrom׳TweeterWithSuccess { (youTubeData) -> Void in
            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                let json = JSON(data: youTubeData)

                if let broadcastsArray = json["result"].array {

                    for broadcastDict in broadcastsArray{
                        var broadcastID: String? = broadcastDict["id"].string
                        var broadcastURL: String? = broadcastDict["broadcast"].string

                        DataManager.getBroadcastDetailsFromYouTubeWithSuccess(broadcastURL!) { (youTubeData) -> Void in
                            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                                let json2 = JSON(data: youTubeData)
                                if let broadcastDetailsDict = json2["result"].dictionary {

                                    var cover: String! = broadcastDetailsDict["cover"]!.string
                                    var caption: String! = broadcastDetailsDict["caption"]!.string
                                    var broadcast = BroadcastModel(id: broadcastID, broadcastURL: broadcastURL, cover: cover, caption: caption)
                                    broadcasts.append(broadcast)
                                    **println(broadcasts)**
                                }
                            }
                        }
                    }
                }
            }
        }
        **return broadcasts**
    }
}

查看答案后,我将代码更改为:

import UIKit

public class BroadcastRequest {

    func requestNewBroadcasts() {
        var broadcasts = [BroadcastModel]()
        var sectionsOfBroadcasts = [[BroadcastModel]]()
        DataManager.getBroadcastsFrom׳TweeterWithSuccess { (youTubeData) -> Void in
            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                let json = JSON(data: youTubeData)

                if let broadcastsArray = json["result"].array {

                    for broadcastDict in broadcastsArray{
                        var broadcastID: String? = broadcastDict["id"].string
                        var broadcastURL: String? = broadcastDict["broadcast"].string

                        DataManager.getBroadcastDetailsFromYouTubeWithSuccess(broadcastURL!) { (youTubeData) -> Void in
                            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                                let json2 = JSON(data: youTubeData)
                                if let broadcastDetailsDict = json2["result"].dictionary {

                                    var cover: String! = broadcastDetailsDict["cover"]!.string
                                    var caption: String! = broadcastDetailsDict["caption"]!.string
                                    var broadcast = BroadcastModel(id: broadcastID, broadcastURL: broadcastURL, cover: cover, caption: caption)
                                    broadcasts.append(broadcast)
                                    println(broadcasts)
                                }
                            }
                        }
                    }
                }
            }
        }
        **TableViewController.requestFinished(broadcasts)**
    }
}

并且在 class TableViewController-

public class TableViewController: UITableViewController {

...
...


    public func requestFinished(requestedBroadcasts: [BroadcastModel]) {
       self.broadcasts = requestedBroadcasts
       self.tableView.reloadData()
    }

现在我得到错误: 无法使用类型为“([(BroadcastModel)])”

的参数列表调用 'requestFinished'
由于 DataManager.getBroadcastsFromTweeterWithSuccess 的关闭,

return broadcasts 之前 执行了将数据附加到数组的循环。

您的代码有误。在闭包中的代码甚至还没有开始执行之前,立即调用 dispatch_async returns 这样的 GCD 函数。这就是并发编程的本质。

您不能编写发出调用然后在下一行得到答案的异步代码。相反,您需要更改逻辑,以便调用分派异步,然后调用 return 并在块完成之前忘记请求。然后将处理结果的代码放在闭包中。您可以在闭包中添加一个调用,在主线程上调用您的应用程序以通知它处理已完成。

编辑:

您的新代码有多个问题:

您对 dispatch_async 的调用正在使用主队列,这是主线程上的一个队列。如果您的目标是让此代码在后台达到 运行,则此代码无法做到这一点。

TableViewController.requestFinished(broadcasts)的调用还是在错误的地方。在获取数据后,它需要位于块内。它也需要在主线程上执行。

TableViewController.requestFinished(broadcasts) 的调用似乎是向 TableViewController class 而不是向 TableViewController class 的实例发送消息,这是错误的。除非您的块有权访问您要将消息发送到的 TableViewController 实例,否则您无法解决此问题。

您或许应该重构您的 requestNewBroadcasts 方法,如下所示:

  • 不要return任何东西。 (在异步块完成之前,结果将不可用。)
  • 让 requestNewBroadcasts 接受一个完成块(或者更确切地说是一个闭包,因为它们在 Swift 中被调用)。完全摆脱 TableViewController.requestFinished(broadcasts) 调用,而是让您的网络代码在网络请求完成后调用完成块。
  • 调用 dispatch_async 使用后台队列而不是主队列,这样您的任务实际上 运行 秒在后台。
  • 让您的 requestNewBroadcasts 方法调用主线程上的完成块。

其中的每一个步骤都需要您的工作和研究。

弄清楚如何将闭包添加为参数需要深入挖掘。 (参见 Swift Programming Language iBook。解释得很好。)

弄清楚如何获得后台队列需要花功夫。 (请参阅 GCD 上的 Xcode docs 文档。特别是 dispatch_get_global_queue)

弄清楚如何从后台线程调用主线程需要研究(再次参见 GCD 上的 Xcode 文档。提示:您当前对 dispatch_async 的调用正在发送您的阻塞到主线程上的队列。)。