4

我有一个显示图像的 UITableView。每个单元格都有一个图像,每次加载一个单元格时,我都会在后台调用一个选择器(来自 cellForRowAtIndexPath),如下所示:

[self performSelectorInBackground:@selector(lazyLoad:) withObject:aArrayOfData];

唯一的问题是有时我会崩溃(因为我在后台更改数据,同时试图在其他地方读取数据)。这是错误:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CALayerArray: 0xce1a920> was mutated while being enumerated.'

在后台更新数据时,我应该将其移动到主选择器并更改它吗?或者我应该以不同的方式调用 @selector() 吗?

谢谢!

4

3 回答 3

3

如果您可以将操作留在主线程上并且没有滞后或问题,那么您就完成了。

但是:让我们假设您已经这样做并遇到了问题。答案是:不要延迟加载中修改数组。切换到主线程修改数组。在这里查看布拉德的答案:

https://stackoverflow.com/a/8186206/8047

一种使用块的方法,因此您可以将对象发送到主队列(您可能还应该首先使用 GCD 来调用延迟加载,但这不是必需的)。

于 2012-04-21T04:27:47.830 回答
1

You can use @synchronized blocks to keep the threads from walking over each other. If you do

@synchronized(array)
{
  id item = [array objectAtIndex:row];
}

in the main thread and

@synchronized(array)
{
  [array addObject:item];
}

in the background, you're guaranteed they won't happen at the same time. (Hopefully you can extrapolate from that to your code—I'm not sure what all you're doing with the array there..)

It seems, though, like you'd have to notify the main thread anyway that you've loaded the data for a cell (via performSelectorOnMainThread:withObject:waitUntilDone:, say), so why not pass the data along, too?

于 2012-04-21T05:57:46.687 回答
0

Given the term 'lazy load' I am assuming that means you are pulling your images down from a server. (If the images are local then there is really no need for multithreading).

If you are downloading images off a server I would suggest using something along these lines (using ASIHTTPRequest)

   static NSCache *cellCache; //Create a Static cache

    if (!cellCache)//If the cache is not initialized initialize it
    {
        cellCache = [[NSCache alloc] init];
    }
    NSString *key = imageURL;
    //Look in the cache for image matching this url
    NSData *imageData = [cellCache objectForKey:key];

    if (!imageData)
    {
        //Set a default image while it's loading
        cell.icon.image = [UIImage imageNamed:@"defaultImage.png"];'

        //Create an async request to the server to get the image
        __unsafe_unretained ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:imageURL]];


        //This code will run when the request finishes
        [request setCompletionBlock:^{
            //Put downloaded image into the cache
            [cellCache setObject:[request responseData] forKey:key];
            //Display image
            cell.icon.image = [UIImage imageWithData:[request responseData]];
        }];
        [request startAsynchronous];
    }
    else 
    {
        //Image was found in the cache no need to redownload
        cell.icon.image = [UIImage imageWithData:imageData];
    }
于 2012-04-21T05:57:01.967 回答