我有一个显示图像的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()吗?
谢谢!
答案 0 :(得分:3)
如果您可以将操作留在主线程上并且没有任何问题,也不会出现问题。
然而:我们假设您已经完成了这项工作并遇到问题。答案是:不要在延迟加载中修改数组 。切换到主线程来修改数组。请参阅Brad的答案:
https://stackoverflow.com/a/8186206/8047
有关使用块执行此操作的方法,因此您可以将对象发送到主队列(您可能也应该首先使用GCD来调用延迟加载,但这不是必需的。)
答案 1 :(得分:1)
您可以使用@synchronized块来防止线程相互走过。如果你这样做
@synchronized(array)
{
id item = [array objectAtIndex:row];
}
主线程中的和
@synchronized(array)
{
[array addObject:item];
}
在后台,你保证不会同时发生。 (希望你可以从那里推断到你的代码 - 我不确定你在那里用数组做什么..)
但是,似乎你必须通知主线程无论如何你已经加载了一个单元格的数据(通过performSelectorOnMainThread:withObject:waitUntilDone :,比如说),所以为什么不传递数据呢? ?
答案 2 :(得分:0)
鉴于术语“延迟加载”,我假设这意味着您要从服务器中提取图像。 (如果图像是本地的,则实际上不需要多线程)。
如果您要从服务器下载图像,我建议您使用这些内容(使用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];
}