如何使循环等到块已完成? [英] How to make loop wait until block has finished?

查看:123
本文介绍了如何使循环等到块已完成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

PFFiles 的数组,我从parse检索。在 PFFiles 必须转换成图像,我试图将它们转换在一个循环中,但是;

变换图像的阵列必须是在阵列的包含 PFFiles 的顺序相同。

的问题是,是循环运行,并导致所有的块来触发,那么它们都装载在同一时间从而在 imageArray 将导致不同为了 PFFiles 的原始数组,因为如果一个对象完成了previous对象之前加载,它将被添加到previous一前的阵列,使得人们走出去的顺序。

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

   - (无效){organizePhotos
    对于(PFFile *在pictureArray图片){
        //此块负荷,在循环中的下一个索引之后被调用,从而导致数组失灵。
        [图片getDataInBackgroundWithBlock:^(NSData的*数据,NSError *错误){
            [imageArray ADDOBJECT:[UIImage的imageWithData:数据]];
            [个体经营savePhotos]
        }];
    }
}

以上是我的code。反正我有可以使循环等待,直到 getDatanBackgroundWithBlock 完成加载?


解决方案

  

的问题是,是循环运行,并导致所有的块来触发,那么它们都装载在同一时间


这是的不是问题的,这是件好事 - 为什么调用是异步的是它可以采取任意长的时间才能完成的原因。如果你有多个下载做然后再同时做可能是一个巨大的胜利。


  

因此​​imageArray将导致不同的顺序PFFiles的原始数组,因为如果一个对象完成了previous对象之前加载,它会在previous一前添加到阵列中,使得它走出去的顺序。


这是问题,并且可以以许多方式来解决。

当你正在使用数组,这里是一个简单的基于阵列的解决方案:先建立自己正确大小的数组,并用null填充,表示图像尚未到来:

(所有code直接键入答案,当作伪code和期待一些错误)

  NSUInteger numberOfImages = pictureArray.length;NSMutableArray里* downloadedImages = [NSMutableArray的arrayWithCapacity:numberOfImages];
//你不能设置一个特定的元素,如果它是过去一个数组的末尾
//因此pre-用空值填充数组
NSUInteger数= numberOfImages;
而(count--大于0)
   [downloadedImages ADDOBJECT:[NSNull空]];

现在你有你的pre填充阵列只需要修改现有的循环下载的镜像写入正确的索引:

 为(NSUInteger九= 0;九< numberOfImages;九++)
{
    PFFile *图片= pictureArray [九]
    [图片getDataInBackgroundWithBlock:^(NSData的*数据,NSError *错误){
        dispatch_async(dispatch_get_main_queue(),
                       ^ {[imageArray replaceObjectAtIndex:九
                                                withObject:[UIImage的imageWithData:数据]
                        });
        [个体经营savePhotos]
    }];
}

使用 dispatch_async 这里是为了确保不会有并发更新阵列。

如果你想知道当所有图像下载完毕后,你可以检查该 dispatch_async 块中,例如它可以,因为它是在主线程上运行的安全递增计数器和调用方法/问题的通知/调用块当所有的图像下载。

您还可能通过使用数组,并试图保持按位置不同有关项目的阵列使事情对自己更难。字典可以为您节省了一些麻烦,比如每个 PFFile 对象presumably的涉及到不同的URL,而URL是一本字典是非常有效的关键。所以,你可以做到以下几点:

 的NSMutableDictionary * imageDict =新的NSMutableDictionary];对于(PFFile *在pictureArray图片){
    [图片getDataInBackgroundWithBlock:^(NSData的*数据,NSError *错误){
        dispatch_async(dispatch_get_main_queue(),
                       ^ {[imageDict的setObject:[UIImage的imageWithData:数据] forKey:picture.URL];
                        };
        [个体经营savePhotos]
    }];
}

和您可以找到由在字典中查找其网址为 PFFile 实例的图像 - 这将返回如果图像尚未加载。

有其他的解决方案,你可能要考虑让你的code多个异步。不管你做什么尝试调用异步code同步是不是一个好主意,并会影响用户体验。

心连心

I have an array of PFFiles I retrieve from parse. The PFFiles must be converted to images and I am trying to convert them in a loop, however;

The array of converted images must be in the same order of the array containing the PFFiles.

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.

Instead, I would like a loop where it loops through every object in the array, however it doesn't loop onto the next object until the getDataInBackgroundBlock has finished loading and the current object has been added to the new array.

-(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];
        }];
    }
}

Above is my code. Is there anyway I can make the loop wait until the getDatanBackgroundWithBlock finishes loading?

解决方案

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

That is not a problem, it is good - the reason why the call is asynchronous is it can take an arbitrarily long time to complete. If you've got multiple downloads to do then doing then concurrently can be a big win.

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.

This is the problem and can be addressed in a number of ways.

As you are using arrays, here is a simple array based solution: first create yourself an array of the right size and fill it with nulls to indicate the image hasn't yet arrived:

(all code typed directly into answer, treat as pseudo-code and expect some errors)

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]];

Now you have your pre-filled array just modify your existing loop to write the downloaded image into the correct index:

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];
    }];
}

The use of dispatch_async here is to ensure there are not concurrent updates to the array.

If you wish to know when all images have been downloaded you can check for that within the dispatch_async block, e.g. it can increment a counter safely as it is running on the main thread and call a method/issue a notification/invoke a block when all the images have downloaded.

You are also possibly making things harder on yourself by using arrays, and trying to keep items in different arrays related by position. Dictionaries could save you some of the hassle, for example each of your PFFile objects presumably relates to a different URL, and a URL is a perfectly valid key for a dictionary. So you could do the following:

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];
    }];
}

And your can locate the image for a PFFile instance by looking up its URL in the dictionary - which will return nil if the image is not yet loaded.

There are other solutions, and you might want to look into making your code more asynchronous. Whatever you do trying to call asynchronous code synchronously is not a good idea and will impact the user experience.

HTH

这篇关于如何使循环等到块已完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆