如何使循环等待直到块完成?

我有一个从解析中检索的PFFiles数组。 必须将PFFiles转换为图像,然后尝试将它们转换为循环;

转换后的图像数组必须与包含PFFiles的数组的顺序相同。

问题是,循环运行并导致所有块都被触发,然后它们全部同时加载,因此imageArray将导致与原始PFFiles数组不同的顺序,因为如果一个对象在完成之前完成加载在上一个对象之前,它将被添加到前一个对象之前的数组中,使其失序。

相反,我想循环它循环遍历数组中的每个对象,但是它不会循环到下一个对象,直到getDataInBackgroundBlock完成加载并且当前对象已添加到新数组。

 -(void)organizePhotos { for (PFFile *picture in pictureArray) { //This block loads, after the next index in the loop is called, causing the array to be out of order. [picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { [imageArray addObject:[UIImage imageWithData:data]]; [self savePhotos]; }]; } } 

以上是我的代码。 无论如何我可以让循环等到getDatanBackgroundWithBlock完成加载?

问题是,循环运行并导致所有块触发,然后它们全部同时加载

不是问题 ,它很好 – 调用异步的原因是它可能需要任意长的时间才能完成。 如果你有多次下载,那么同时进行可能是一个巨大的胜利。

因此,imageArray将导致与原始PFFiles数组不同的顺序,因为如果一个对象在前一个对象之前完成加载,它将被添加到前一个对象之前的数组中,从而使其失序。

这是问题,可以通过多种方式解决。

当您使用数组时,这是一个基于数组的简单解决方案:首先创建一个正确大小的数组,并用空值填充它以指示图像尚未到达:

(所有代码直接输入到答案中,视为伪代码并期望出现一些错误)

 NSUInteger numberOfImages = pictureArray.length; NSMutableArray *downloadedImages = [NSMutableArray arrayWithCapacity:numberOfImages]; // You cannot set a specific element if it is past the end of an array // so pre-fill the array with nulls NSUInteger count = numberOfImages; while (count-- > 0) [downloadedImages addObject:[NSNull null]]; 

现在你有了预先填充的数组,只需修改你现有的循环就可以将下载的图像写入正确的索引:

 for (NSUInteger ix = 0; ix < numberOfImages; ix++) { PFFile *picture = pictureArray[ix]; [picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ [imageArray replaceObjectAtIndex:ix withObject:[UIImage imageWithData:data] }); [self savePhotos]; }]; } 

这里使用dispatch_async是为了确保没有对arrays的并发更新。

如果您想知道何时下载了所有图像,您可以在dispatch_async块中检查它,例如它可以安全地递增计数器,因为它在主线程上运行并调用方法/发出通知/调用块时全部图像已下载。

您也可能通过使用数组来使自己更难,并尝试将项目保存在与位置相关的不同数组中。 字典可以为您节省一些麻烦,例如您的每个PFFile对象可能与不同的URL有关,而URL是字典的完全有效的键。 所以你可以做到以下几点:

 NSMutableDictionary *imageDict = [NSMutableDictionary new]; for (PFFile *picture in pictureArray) { [picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ [imageDict setObject:[UIImage imageWithData:data] forKey:picture.URL]; }; [self savePhotos]; }]; } 

您可以通过在字典中查找其URL来找到PFFile实例的图像 - 如果图像尚未加载,则返回nil

还有其他解决方案,您可能希望考虑使代码更加异步。 无论你试图同步调用异步代码都不是一个好主意,并会影响用户体验。

HTH

理想情况下,您应该使用同步方法; 但是,您要求的行为可以通过Grand Central Dispatch实现。

首先,使用dispatch_group_create()创建dispatch_group_t 。 我们称之为asyncGroup

然后,在调用异步方法之前,调用dispatch_group_enter(asyncGroup) 。 这会增加组中呼叫数的计数器。

然后,在异步块的末尾,调用dispatch_group_leave(asyncGroup)以减少组中调用次数的计数器。

最后,在调用异步方法之后,调用dispatch_group_wait(asyncGroup, timeout)暂停线程执行,直到组计数器达到零。 由于您递增,进行调用,然后等待,循环将仅在运行异步块时继续。 只需确保超时时间超过操作所需的时间。

您可以使代码同步:

 -(void)organizePhotos { for (PFFile *picture in pictureArray) { [imageArray addObject:[UIImage imageWithData:[picture getData]]]; [self savePhotos]; } } 

并在后台运行它:

 [self performSelectorInBackground:@selector(organizePhotos) withObject:nil]; 

您可以使用GCD方法,即使用dispatch_group。 因此,在启动异步任务之前,调用dispatch_group_enter,然后在异步任务完成时调用dispatch_group_leave,然后可以创建一个dispatch_group_notify,它将在异步任务完成时调用。 你可以将它与完成块模式相结合(无论如何,这对异步方法来说是一个好主意):

这是类似的问题。 也许它也可能有用: 如何等待具有完成块的方法(所有在主线程上)?

您可以使用dispatch_async方式,使用信号量,组来实现,但为了您的需要,更好的做法是使用块来获取响应并执行您需要的操作。

这样做:

 -(void)organizePhotos:(void(^)(BOOL responseStatus))status { for (PFFile *picture in pictureArray) { //This block loads, after the next index in the loop is called, causing the array to be out of order. [picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { [imageArray addObject:[UIImage imageWithData:data]]; status(YES); }]; } 

}

所以你接下来的电话就可以做到:

 __weak typeof(self) weakSelf = self; [self organizePhotos:^(BOOL responseStatus) { if(responseStatus){ [weakSelf savePhotos]; } }]; 

或者如果你不想为你的方法创建额外的参数响应,你可以这样做:

 -(void)organizePhotos { __weak typeof(self) weakSelf = self; void (^ResponseBlock)(void) = ^{ [weakSelf savePhotos]; }; for (PFFile *picture in pictureArray) { //This block loads, after the next index in the loop is called, causing the array to be out of order. [picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { [imageArray addObject:[UIImage imageWithData:data]]; ResponseBlock(); }]; } 

}

而另一点是你在内部调用“self”时出错了,这可能会导致你保留循环并且这是一个不好的做法,看看我的代码你应该怎么做。