Ios 使用串行调度队列调用接受完成块作为参数的方法

Ios 使用串行调度队列调用接受完成块作为参数的方法,ios,objective-c,objective-c-blocks,grand-central-dispatch,dispatch-async,Ios,Objective C,Objective C Blocks,Grand Central Dispatch,Dispatch Async,我有一个方法: -(void) dataForRequest:(NSMutableURLRequest*)url withCallback:(void (^)(NSData* data))callbackBlock` 它从后台的URL获取数据,然后使用收到的数据调用callbackBlock 我在一个串行队列中调用它,如: dispatch_async(my_serial_queue(), ^(void) { [dataForRequest:SOME_REQUEST withCall

我有一个方法:

-(void) dataForRequest:(NSMutableURLRequest*)url withCallback:(void (^)(NSData* data))callbackBlock` 
它从后台的URL获取数据,然后使用收到的数据调用
callbackBlock

我在一个串行队列中调用它,如:

dispatch_async(my_serial_queue(), ^(void) {
    [dataForRequest:SOME_REQUEST withCallback:^(NSData *data) {
      // do something with data here.
}];

});
我在串行队列上执行此操作的原因是,我希望请求一次调用一个,按照发出的顺序进行调用

但是我面临的问题是,请求是按顺序发出的,但是我传递给方法的
callbackBlock
不是被调用的order

例如,
request_1
request_2
request_3
以正确的顺序提交到串行队列,但是
callBackBlock_1
callBackBlock_2
callBackBlock_3
的执行顺序不同

我理解为什么会发生这种情况,但我无法找到任何解决办法。
任何帮助都将不胜感激。谢谢

有很多可能的实现,但这里的基本原则是,您希望在发出下一个请求之前完成一个请求


如注释中所述,您可以创建一个NSO操作来封装每个请求。只有在调用完成块后,才能发出操作完成的信号。这种方法可能是最好的,也最不容易出现错误。

这里有一个用Swift实现的解决方案,完全基于GCD。这种方法是完全异步的,除了最后一条语句
dispatch\u semaphore\u wait(sem,dispatch\u TIME\u FOREVER)
之外,它的存在只是为了在所有任务和延续完成之前保持主线程的活动状态

将此示例移植到Objective-C应该不会太困难。关键可能是正确获取dispatch_组的导入引用

下面的代码创建了十个名为“1”、“2”、“….”和“10”的异步任务。每个任务的持续时间在0到8秒之间是随机的。每个任务的继续将打印其名称

因此,输出应该是这样的,即名称将按照与计划相同的顺序打印-无论持续时间如何。在这种情况下,名称已经打印出来,但仍有任务在执行

import Foundation


let queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL)


func task(duration: Double, completion: ()->()) {
    let t = dispatch_time(DISPATCH_TIME_NOW, (Int64)(duration * Double(NSEC_PER_SEC)))
    dispatch_after(t, dispatch_get_global_queue(0, 0)) {
        completion()
    }
}


func test(params: [String], continuation: ()->()) {
    let g0 = dispatch_group_create()
    dispatch_group_enter(g0)
    var g_prev = g0

    for s in params {
        let g = dispatch_group_create()
        dispatch_group_enter(g)
        let t = Double(arc4random() % 8)
        print("schedule task \"\(s)\" with duration \(t)")
        let gp = g_prev
        task(t) {
            print("finished task \"\(s)\"")
            dispatch_group_notify(gp, queue) { [g]
                print(s)
                dispatch_group_leave(g)
            }
        }
        g_prev = g

    }
    dispatch_group_leave(g0)

    dispatch_group_notify(g_prev, dispatch_get_global_queue(0, 0)) {
        continuation()
    }
}


let sem = dispatch_semaphore_create(0)
test(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]) {
    print("finished")
    dispatch_semaphore_signal(sem)
}

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

我不知道用GCD怎么做。但是您可以使用NSOperationQueue,在这里您可以轻松地指定NSOperations之间的依赖关系,以便第二个任务仅在第一个任务完成后开始。@AlokRao您的解决方案并不完全相同:最初的想法是按顺序调用请求(例如使用FIFO),然后并发运行它们。最后,当全部完成时,按顺序处理接收到的数据。一种潜在的优化方法是在数据到达时进行处理,如果之前的数据已经处理过。使用NSOperation执行此操作是可能的,但非常麻烦。使用GCD也可以做到这一点——但需要技巧;)您会说“可能是这样的,名称已经打印出来,但仍有任务在执行。”但在我的情况下,我需要在任务调用callbackBlock之前完成任务。。因为您看到,
callBackBlock
使用从URL获取的数据。@SagarD上述方法可用于执行以下操作:给定URL数组。为每个URL创建一个从服务器获取数据的异步任务。按顺序安排任务,然后按顺序处理返回的数据(即,处理1.URL中的数据,然后处理2.URL中的数据,等等)。处理完所有数据后,
test
函数调用其完成处理程序。这是你想要的吗?注意:这是实际并发执行的请求数量的实现细节。如果使用
NSURLSession
,则可以配置最大并发请求数。