2010-07-30 18 views
5

Tworzę aplikację słownikową dla iPhone'a, która daje wynik, gdy użytkownicy piszą. Używam wątków (NSThread) do aktualizacji UITableView tak, aby główny wątek nie był blokowany.Aktualizacja UITableView przy użyciu wątków

Jednak zdarza się awaria, gdy UITableView pyta źródło danych o liczbę wierszy (tableView: numberOfRowsInSection :) i zwracam, powiedzmy, 10. Następnie prosi o źródło danych dla komórek 0-9 (tableView: cellForRowAtIndexPath :). Ale do czasu, gdy prosi o komórkę 7, źródło danych już się zmieniło, a teraz ma tylko 5 wierszy, co powoduje awarię.

Oto jak rozwiązać ten problem:

tworzę NSLock w metodzie startowej.

A oto co źródło danych wygląda następująco:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return [results count]; 
} 


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CellIdentifier = @"Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    [lock lock]; 
    if (indexPath.row < [results count]) { 
     cell.textLabel.text = [results objectAtIndex:indexPath.row]; 
    } 
    [lock unlock]; 

    return cell; 
} 

i tutaj jest kod, który używam do aktualizacji tabeli:

[tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; 

To rozwiązuje problem zderzenia całkowicie. Uważam jednak, że może to nie być wydajne, ponieważ źródło danych musi blokować/odblokowywać za każdym razem, gdy zostanie poproszony o komórkę. A sprawa, o której wspomniałem powyżej, nie zdarza się tak często. Czy ktoś ma lepszy pomysł, jak skutecznie rozwiązać ten problem?

Dziękuję bardzo!

+2

Nie należy zaktualizować UI w innym wątku. – rickharrison

+0

Rick, muszę wyszukać wynik w innym wątku, ponieważ metoda wyszukiwania jest dość powolna i nie chcę, aby blokowała główny wątek. Czy mógłbyś wyjaśnić, w jaki sposób mogę to osiągnąć bez używania innego wątku? – ifvc

+0

przyspieszyć.co na ziemi przeszukujesz ?! – mvds

Odpowiedz

2

Dlaczego używałbyś do tego oddzielnego wątku? Jak długo trwa wyszukiwanie? 0,1 sekundy? Jak to się ma do czasu, jaki użytkownik musiał poświęcić na przerwanie pisania i patrzenie na ekran?

Nie komplikuj zbytnio rzeczy! (Wezmę to, jeśli wyszukiwanie trwa dłużej niż 0,7 sekundy i nie można go zoptymalizować ;-)

+0

Szczerze mówiąc, zajmuje to około 0,2-0,3 sekundy. (Oryginalne wyszukiwanie za pomocą SQLite/FTS3 zajmuje około 0,5 sekundy, więc zoptymalizowałem je za pomocą zwykłego tekstu i wyszukiwania binarnego). Mimo że jest małe, w połączeniu z czasem potrzebnym na aktualizację stołu, wciąż jest wystarczająco duże, aby mogło dać czuję, że klawiatura jest "lepka". – ifvc

+0

Więc patrzysz na jakieś 1-metrowe rekordy ... Skupiłbym się na optymalizacji wyszukiwania danych, przyniesie to również korzyści dla użytkownika. Po pierwsze, jeśli użytkownik wpisze "b", nie ma powodu, aby natychmiast gotować całą listę "b". – mvds

+0

Próbowałem tego z SQLite i nie robiło to różnicy, nawet jeśli używam "LIMIT 1". Domyślam się, że może to być spowodowane tym, że SQLite najpierw szuka całej listy, a następnie ją sortuje (nawet jeśli nie używam żadnego "ORDER BY"), a na końcu zwraca wynik. Dlatego właśnie przełączyłem się na wyszukiwanie binarne, więc mogę wyszukać indeksy dolnego i górnego, a następnie zapytać wynik w razie potrzeby. – ifvc

4

Nie próbuj aktualizować interfejsu z wątku tła. To nie zadziała.

+1

Dlatego używam [tableView performSelectorOnMainThread: @selector (reloadData) withObject: nil waitUntilDone: NO]; i działa (w tym sensie, że nie ma już awarii). Ale chciałbym, aby działało szybciej, nie blokując/odblokowując za każdym razem, gdy komórka jest pytana. – ifvc

1

Właśnie tutaj jest przydatna odpowiedź, oto moja odpowiedź na inne, podobne pytanie.

Nie wiedząc więcej o Twojej aplikacji, myślę, że jednym z lepszych rozwiązań byłoby utrzymanie tablicy w głównym wątku i przesłanie jej z powrotem, gdy inny wątek musi dokonać zmiany. W ten sposób:

dispatch_async(dispatch_get_main_queue(), ^{ 
       [array addObject:object]; 
       [tableView reloadData]; 
      }); 

Oczywiście, można uzyskać znacznie bardziej skomplikowane dzięki funkcji API wysyłki, ale obsługuje ona blokowanie i wszystko dla Ciebie. Zdecydowanie bardziej elegancki niż przy użyciu NSLock. Działa to jednak tylko w systemie iOS 4 lub nowszym.

Powiązane problemy