我想将一些数据与Web服务同步。对于每个项目,我必须进行异步调用。
我希望在每个项目同步时调用完成块。对于每个项目,我能够执行完成块。现在,我不知道怎么做的好方法。
这是界面:
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
for (int i = 0, n = [items count]; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
// What do do here?
}];
}
// And/or here?
}
-(void) synchronizeItemOnComplete:(CompleteBlock) block {
// do something
block();
}
如何等待同步然后执行阻止?
我试过这样的事情:
NSArray* items = // get items
__block int countOfItemsUntilDone = [items count];
for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
countOfItemsUntilDone--;
}];
}
dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
while (countOfItemsUntilDone > 0) {
usleep(1000); // wait a little bit
}
block();
});
dispatch_release(queue);
但我认为这是一个非常糟糕的方式。有什么想法吗?
答案 0 :(得分:4)
不是在等待计数器等于零的循环中旋转,而是每次递减时检查计数器值,然后在达到零时触发事件。
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
__block NSUInteger remaining = [items count];
for (ItemClass* item in items) {
[self synchronizeItemImage:item onComplete:^{
--remaining;
if (remaining == 0) {
block();
}
}];
}
}
要解释为什么感觉不对,你在这里做的两件事你应该做或不做或很少:
使用后台队列。这很难并容易出错。如果没有阅读关于编写并发代码的 lot ,请不要这样做。如果操作阻塞了大量时间(例如,从磁盘读取文件或执行密集计算),您也只需要这样做。除非你有充分的理由(例如,可衡量的性能问题),否则不要假设你需要这样做。
在循环中旋转,检查变量是否有变化并调用睡眠。你应该从不这样做。
此外,如果您循环遍历数组中的元素,for ... in
语法在每个索引上调用objectAtIndex:会更好(并且可能更有效)。
答案 1 :(得分:2)
永远不要在这样的不同线程中检查或减少共享内存,它可能会导致比赛。使用调度组来完成您正在做的事情。
dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();
for (ItemClass *item in items) {
dispatch_group_async(itemsGroup, myBGQueue, ^{
[self synchronizeItemImage:item];
});
}
/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);
dispatch_release(itemsGroup);
答案 2 :(得分:1)