8

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

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

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

仅当图像尚未脱机可用时,才会发生上述所有情况。下载后,将使用本地存储中的图像。

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

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

 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)当用户点击一个类别时,我需要先下载这些图像,因为它们必须立即显示给用户。我不确定如何中断 missingImages 调用并告诉它在其他图像之前开始下载某些图像。

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

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

崩溃日志如下所示

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)

提前致谢。

4

1 回答 1

3

关于崩溃,我猜您的应用程序由于以下两个选项之一而被终止:

  1. 应用程序变得无响应(因此没有响应 iOS 哨兵进程);

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

为了澄清实际发生的情况,您应该提供有关崩溃的更多信息(控制台日志)。在任何情况下,修复都会以每个可能 10 或 20 个的块加载图像(如果你愿意,你甚至可以一个一个地去,如果你愿意,我看不出有什么问题)。

关于第二点,这个呢:

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

  2. 在开始下载“离线”图像之前,您同时通过“更高优先级”下载检查图像是否已经下载。

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

编辑:

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

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 (...)循环不存在;您只需查询下一个图像以下载并下载它;在下载之前检查它是否已经在那了。

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

希望能帮助到你。

于 2012-06-24T10:28:06.493 回答