2012-04-21 3 views
4

У меня есть UITableView, который отображает изображения. Каждая клетка имеет образ и каждый раз, когда клетка нагрузку, звонит селектор (от cellForRowAtIndexPath) в фоновом режиме, как это:Правильный способ многопоточности в объективе-c?

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

Единственная проблема заключается в том, что иногда я получаю аварию (потому что я меняюсь данные в фоновом режиме, пока он пытается прочитать в другом месте). Вот ошибка:

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

При обновлении данных в фоновом режиме, необходимо переместить ее на основной селектор и изменить его? Или я должен называть @selector() по-другому?

Спасибо!

ответ

3

Если вы можете оставить операцию на основной нити и не иметь никакой лаги и проблем, которые вы сделали.

Однако: Предположим, вы уже это сделали и столкнулись с проблемами. Ответ: не изменяйте массив в ленивой нагрузкой. Перейдите в основной поток, чтобы изменить массив. Смотрите ответ Брэда здесь:

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

способ сделать это с блоками, так что вы можете отправить свои объекты к основной очереди (вы, вероятно, следует также использовать GCD для вызова ленивых нагрузки в первое место, но это необязательно).

+0

Я попытался использовать блоки GCD, и загруженные изображения ДЕЙСТВИТЕЛЬНО медленно выскочили сразу (вместо загрузки по отдельности). Я пошел с вашим первым предложением, чтобы использовать 'performSelectorOnMainThread:' при изменении данных. Фотографии загружаются мгновенно, и я не мог просить ничего лучше. Надеюсь, у меня больше нет проблем. Благодаря! – iosfreak

+0

@ phpnerd211 Чтобы переключиться на основной поток, вам придется использовать 'dispatch_async (dispatch_get_main_queue(), block);', но 'performSelectorOnMainThread' тоже в порядке. Рад помочь. –

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]; 
    } 
1

Вы можете использовать @synchronized блоки, чтобы держать нити от ходьбы друг от друга. Если вы

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

в главном потоке и

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

в фоновом режиме, вы гарантированно не будет происходить одновременно. (Надеюсь, вы сможете экстраполировать это на свой код - я не уверен, что вы делаете с массивом там).

Кажется, вам, как и вам, нужно было бы уведомить основную нить, вы загрузили данные для ячейки (через performSelectorOnMainThread: withObject: waitUntilDone :, say), так почему бы и не передать данные вместе?

+1

Вы не можете использовать блоки '@ synchronized', потому что вы не можете заблокировать основной поток, или вы вызовете приложение« икать ». –

+1

Блокировка сохраняется только до тех пор, пока требуется добавить элемент в массив. Не будет никакой «икоты». – davehayden

+0

Согласен, в этом случае это, вероятно, тривиально (т. Е. Незаметно). –