AFNetworking + NsOperationQueue - 下载数千张图片

时间:2012-06-24 07:49:40

标签: ios5 nsoperationqueue afnetworking

我正在处理一项任务(仅限iOS5 +),涉及从服务器下载数千张图像。图像属于某些类别,每个类别可以有数百个图像。我需要做的是: -

1)如果应用程序处于活动状态,请确保应用程序在后台下载任何丢失的图像(即使用户正在浏览应用程序中与照片无关的其他某些区域)。

2)当用户点击照片类别时,必须将该类别中的图像作为高优先级下载,因为那些是需要立即可见的图像。

只有当图像尚未离线使用时,才会发生上述所有情况。下载后,图像将从本地存储中使用。

要解决这个问题,我使用的逻辑是: -

1)在AppDelegate.m中,在applicationDidBecomeActive中,我开始下载任何丢失的图像。为此,我进行了核心数据查询,找出缺少的图像,并开始在具有BACKGROUND优先级的线程中下载它们。这样的事情: -

 dispatch_queue_t imageDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(imageDownloadQueue, ^{
    [DataDownloader downloadMissingImages];
});
dispatch_release(imageDownloadQueue);

downloadMissingImages代码如下所示: -

NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
        downloadQueue.maxConcurrentOperationCount = 20;

        for(MyImage *img in matches)
        {
            NSURLRequest *request = [NSURLRequest requestWithURL:img.photoUrl];
            AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) {

                [MyImage imageFromAPI:image inManagedObjectContext:document.managedObjectContext];

                NSLog(@"Successfully downloaded image for %@", img.title);      
            }];

            [downloadQueue addOperation:operation];
        }

这可行,但它会阻止主UI,应用程序会在一段时间后崩溃。这是我尝试下载大约700张图片的时候。随着更多的图像,它肯定会崩溃。

2)当用户点击某个类别时,我需要先下载这些图像,因为它们必须立即显示给用户。我不知道如何中断missImages调用并告诉它开始在其他人之前下载某些图像。

因此,基本上,我需要在后台下载所有丢失的图像,但如果用户正在浏览照片类别,则这些图像必须在下载队列中具有高优先级。

我不知道如何有效地工作。有什么想法吗?

崩溃日志看起来像这样

PAPP(36373,0xb065f000) malloc: *** mmap(size=16777216) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
PAPP(36373,0xb065f000) malloc: *** mmap(size=16777216) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
Jun 24 11:39:45 MacBook-Pro.local PAPP[36373] <Error>: ImageIO: JPEG    Insufficient memory (case 4)

提前致谢。

1 个答案:

答案 0 :(得分:3)

关于崩溃,我猜你的应用程序因两个选项中的任何一个而被杀死:

  1. 应用无响应(因此无法响应iOS哨点流程);

  2. 循环中使用了太多内存,创建了超过700个请求操作。

  3. 为了澄清实际发生的情况,您应该提供有关崩溃的更多信息(控制台日志)。在任何情况下,修复程序都会将图像加载到每个可能包含10或20个的块中(如果你愿意的话,你甚至可以逐个加1,我也不会看到很多问题)。

    关于第二点,这是怎么回事:

    1. 在主线程中下载更高优先级的图像(当然,通过异步下载,以避免阻止UI);

    2. 开始下载&#34;离线&#34;图像,您可以通过优先级较高的&#34;来检查图像是否已经同时下载。下载。

    3. 为了更好地处理第2点,您可能需要对自定义操作进行排队,而不是AFImageRequestOperation,以便在实际下载之前进行检查。

      编辑:

      关于以块的形式下载图像,您可以使用调度组对网络操作进行分组:

      dispatch_group_t group = dispatch_group_create();
      
      <your_core_data_query>
      
      for (...) {
          dispatch_group_enter(group);
      
              NSURLRequest *request = [NSURLRequest requestWithURL:img.photoUrl];
              AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) {
                  [MyImage imageFromAPI:image inManagedObjectContext:document.managedObjectContext];
                  NSLog(@"Successfully downloaded image for %@", img.title);
      
                  dispatch_group_leave(group);     //<== NOTICE THIS
              }];
      
      }
      
      dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
      dispatch_release(group);
      

      在此示例中,我使用调度组对多个异步操作进行分组,并等待它们全部执行;当dispatch_group_wait返回时,您可以执行另一轮(查询核心数据,然后调度操作)。

      关于您的其他问题(如何检查优先级较高的队列是否已下载某个图像),您应该在执行每个AFImageRequestOperation之前执行核心数据查询;一种可能性是派生自己的类并覆盖start方法来进行检查。

      在这两个帐户中,您可以通过一次下载图像来简化所有这些的逻辑(即,for (...)循环不在那里;您只需查询下一个图像以下载并下载它;在下载之前,检查它是否已经存在。

      我建议走这条更容易的道路。

      希望它有所帮助。