在objective-c中正确的多线程方式?

时间:2012-04-21 04:22:35

标签: objective-c multithreading nsmutablearray

我有一个显示图像的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()吗?

谢谢!

3 个答案:

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