ios - 如何让循环等到 block 完成?

标签 ios objective-c arrays loops block

我有一个 PFFiles 数组,我从解析中检索到。 PFFiles 必须转换为图像,但是我正在尝试循环转换它们;

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

问题是,循环运行并导致所有 block 触发,然后它们全部同时加载,因此 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 完成加载吗?

最佳答案

The problem is, is the loop runs and causes all of the blocks to trigger, then they are all loading at the same time

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

thus the imageArray will result in a different order to the original array of PFFiles, because if one object finishes loading before the previous object, it will be added to the array before the previous one, making it go out of order.

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

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

(所有直接输入答案的代码,视为伪代码并预计会出现一些错误)

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是为了保证数组不会并发更新。

如果你想知道所有图像何时下载完毕,你可以在 dispatch_async block 中检查,例如它可以在主线程上运行时安全地递增计数器,并在所有图像下载完毕后调用方法/发出通知/调用 block 。

您还可能通过使用数组让事情变得更难,并试图让不同数组中的项目按位置相关。字典可以为您省去一些麻烦,例如,您的每个 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

关于ios - 如何让循环等到 block 完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32163480/

相关文章:

ios - 像表格一样显示具有动态大小的标签,但不重叠

ios - WebP 格式的图像并不总是在 iOS 上显示

objective-c - 释放没有指针的对象?

python - 使用索引列表切片 n 维 numpy 数组

php数组mysql查询

javascript - 通过提示在数组中添加一个新对象

ios - iOS 中的 TouchBegan 和响应者

ios - Model View Controller 存储 - 在这种情况下我应该创建多个存储对象吗?

ios - 如何以编程方式将 plist 文件添加到Setting.bundle?

ios - UITableView 滚动时 UITableViewCell 动画停止