在主线程中使用完成处理程序设置后台操作的顺序

时间:2016-02-06 01:03:11

标签: ios multithreading swift grand-central-dispatch

每次应用启动时,我都会在后台线程上执行以下方法:

func setup() {
   loadPlayers()
   loadTeams()
}

这些方法中的每一个都调用webService,它也在后台线程中完成,并解析JSON收到的数据以在内存中加载数据。出于某种原因,这些解析数据的过程是在主线程中完成的。

球队由球员组成,因此我必须在加载球队之前设置球员的所有信息。我该怎么做?我尝试了以下代码,但在加载所有播放器之前仍然会调用loadTeams()

func setup() {   
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
       self.loadPlayers()
    })
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
      self.loadTeams()
    })
}

1 个答案:

答案 0 :(得分:2)

听起来,您的loadPlayers()loadTeams()函数是异步发生的。因此,如果您使用dispatch_sync并不重要,因为函数会立即返回,dispatch_sync也会立即返回。

As trick14 said,您需要自己实现对这些函数的回调。我会想象' webService'你正在使用它会实现某种回调系统。

例如,您希望让您的功能执行以下操作:

func loadPlayers(callback: () -> ()) {

    // I don't know what API you're using, but it must implement some form of callback system...

    doAsynchronousNetworkTaskWithCompletion({success in
        callback()
    })
}

然后,您可以简单地将这些任务分派到后台队列,使用回调对下一个任务进行排队。例如:

func setup() {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

        self.loadPlayers({
            self.loadTeams({
                dispatch_async(dispatch_get_main_queue(), {
                    // do UI update
                })
            })
        })
    })

}

这种方法唯一的问题是你会开始创建一个厄运金字塔,这对于提高可读性并不是很好。

要解决此问题,您可以使用dispatch_group来安排在完成给定数量的任务时触发的完成块。由于您的任务是异步的,因此您必须使用函数dispatch_group_enter()dispatch_group_leave()来手动增加和减少正在运行的任务数。

例如:

func setup() {

    let group = dispatch_group_create()

    dispatch_group_enter(group) // Increment the number of tasks in the group
    loadShirts({
        dispatch_group_leave(group) // Decrement the number of tasks in the group
        print("finished shirts")
    })

    dispatch_group_enter(group)
    loadStadiums({
        dispatch_group_leave(group)
        print("finished stadiums")
    })

    dispatch_group_enter(group)
    loadPlayers({
        dispatch_group_leave(group)
        print("finished players")
    })

    // gets fired when the number of tasks in the group reaches zero.
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { 
        self.loadTeams({
            dispatch_async(dispatch_get_main_queue(), {
                // do UI update
                print("finished")
            })
        })
    })
}

还值得注意的是,在主队列中使用dispatch_sync到后台队列并不能保证任务将在后台线程上运行。

因为你在执行此操作时会阻塞主线程,所以GCD通常只需在主线程上运行该代码进行优化(因为转移到另一个线程很昂贵)。 See here for more info.

因此,您希望尽可能使用dispatch_async并在后台操作本身中进行同步,尽管这在您的情况下并不重要,因为您的任务无论如何都是异步的。