2012-03-09 17 views
5

W mojej aplikacji mam do czynienia z dużą ilością danych pochodzących z sieci i parsować ją w zoptymalizowany format lokalny, a następnie zapisać je do bazy danych lub wysłać je do interfejsu użytkownika, jeśli istnieje Interfejs użytkownika czeka na te dane.iOS: Jak debugować opóźnienie interfejsu użytkownika [iPad2 dual-core]

Wiem, że wykonywanie ciężkiej analizy w głównym wątku jest głupie, ponieważ blokuje główny wątek i powoduje, że interakcja interfejsu użytkownika jest dość zła.

Więc tutaj jest to, co starałem się upewnić, że wątek UI jest wolny.

  • Globalna optymalizacja usunąć bardzo powolny kod pomimo tego, co jest w wątku
  • Dolna wszystkie priorytety wątku, tj

    NSOperation.threadPriority=0.1; 
    NSThread.threadPriority=0.1; 
    dispatch_async(dispatch_get_global_queue(PRIORITY_LOW)); 
    

Po tych dwóch etapach, UI jest bardziej gładka, gdy niektórzy ciężka praca jest wykonywana w tle wątków, ale wciąż całkiem podniecony.

Błąd wygląda następująco: Gdy przewijam stół dość płynnie i nagle stół zamarza na około 0,1 sekundy, a następnie działa równie płynnie jak zwykle. Podczas zamrażania widzę impuls procesora w Instrumentach. Ale w osobnym widoku wątku widzę, że główny wątek jest OK, prawie bez zużycia procesora.

  1. Dlaczego więc tabela interfejsu użytkownika nadal się zawiesza?
  2. A dlaczego wątki tła nadal wykonują tyle cykli procesora przy tak ustawionych priorytetach wątków?
  3. W jaki sposób upewniacie się, że interfejs użytkownika zawsze reaguje?
  4. Czy są jakieś przydatne narzędzia do wyróżnienia miejsc powodujących zamrożenie interfejsu użytkownika?
+0

Najlepszym sposobem jest zrobić coś podobnego aplikacji twitter robi. są implementacje, które mogą ci w tym pomóc. [http://blog.leahculver.com/2010/07/iphone-pull-to-refresh.html] – Ashishail

+1

@Ashishail Dzięki za komentarz. Pobranie w celu odświeżenia jest dobrym sposobem na zminimalizowanie ruchu, który nie jest odpowiedni dla mojej aplikacji, ponieważ musi działać w trybie offline. –

+1

ohh ok, pomyślałem, że ur przychodzisz z sieci, dla lokalnych jest wiele rzeczy, które możesz zrobić.sprawdź, czy twoja_własna_przekiernia_zdrowia wraca tak szybko, jak to możliwe. używaj wstępnie wypełnionych tablic, jeśli rekordy ur są w setkach grzywny. jeśli w tysiącach, możesz dokonać wstępnego pobrania na podstawie ostatniego wiersza nazwanego. więc np. najpierw zdobądź 100 wierszy, gdy 50-ty wiersz zostanie wywołany otrzymasz 100 więcej, n tak dalej. – Ashishail

Odpowiedz

3

Dobrym rozwiązaniem jest użycie Grand Central Dispatch (GCD) i Objective-C blocks w połączeniu z lokalną pamięcią podręczną. Po załadowaniu komórki tabeli powinieneś jak najszybciej powrócić z poziomu tableView:cellForRowAtIndexPath: z już skonfigurowaną komórką tabeli z lokalnie przechowywanymi danymi. Wszystkie części komórki, które muszą pobierać dane lub wykonywać ciężkie przetwarzanie, powinny mieć zamiast tego element zastępczy (na przykład wskaźnik aktywności). Ciężki przetwarzanie lub pobierania nastąpi w innym wątku jak ten:

// ...This code is at the end of tableView:cellForRowAtIndexPath: where cell 
// is already set up and you should have access to your cache and your data 
// manager object that will do the processing or downloading for you. 

// Show the placeholder (it will hide when real data is set) 
cell.placeholder.hidden = NO; 
// Check if item is local 
MyDataItem* item = [myCache objectForKey:itemKey]; 
if(item != nil) 
{ 
    // Item is available locally, set up the cell now 
    cell.someImageView.image = item.image; 
    cell.someLabel.text = item.text; 
    cell.placeholder.hidden = YES; 
} 
else 
{ 
    // Item is not in cache, go and get it ready 
    [myDataManager goAndGet:itemKey completion:^(MyDataItem* theItem) 
    { 
     // Put the item in the cache 
     [myCache setObject:theItem forKey:itemKey]; 
     // This block will be executed later and the cell may have been reused 
     // by then, so we need to ask the table view directly for the cell. 
     // This returns nil if cell is not currently visible. 
     MyTableCell* theCell = (MyTableCell*)[tableView cellForRowAtIndexPath:indexPath]; 
     // Set up the table cell 
     if(theCell != nil) 
     { 
      myCell.someImageView.image = theItem.image; 
      myCell.someLabel.text = theItem.text; 
      myCell.placeholder.hidden = YES; 
     } 
    }]; 
} 
return cell; 

Jak widać z kodu powyżej, komórka zwracana jest bardzo szybko, nawet kiedy jest potrzebny jakiś ciężki przetwórstwo. Dzięki temu interfejs użytkownika jest miły i elastyczny, bez żadnych opóźnień, które powodują gwałtowne przewijanie w widoku tabeli. Menedżer danych użyje GCD wykonać przetwarzanie, coś takiego:

- (void)goAndGet:(NSString*)itemKey completion:(void (^)(MyDataItem* theItem))completionBlock 
{ 
    // Note: this uses DISPATCH_QUEUE_PRIORITY_DEFAULT - but you might want to use 
    // a different queue, probably one you created yourself for these tasks. 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^
    { 
     // Do heavy processing or downloading here 
     MyDataItem* item = [heavyProcessor doProcessingForItem:itemKey]; 

     // The completion block must happen on the main queue 
     dispatch_async(dispatch_get_main_queue(),^
     { 
      // Call the completion block 
      completionBlock(item); 
     }); 
    }); 
} 

Należy pamiętać, że przede wszystkim kod zakłada używasz ARC :)

+0

Dziękujemy! Przepraszam, że tak późno sprawdziłem odpowiedź. Tak, w końcu używam podobnego rozwiązania, jak twoje. Wdrażam renderowanie w tle, a przewijanie jest teraz bardzo płynne :) –

+1

Bez problemu - mam nadzieję, że była to dla ciebie przydatna odpowiedź. Nawet jeśli działasz w inny sposób, naprawdę polecam czytanie o blokach i GCD (zobacz linki na górze tej odpowiedzi). GCD bardzo ułatwia wykonywanie zadań w tle, które trzeba uruchomić na innym wątku, ale oddzwoni do głównego wątku, a także bloki naprawdę pomagają w utrzymaniu tymczasowych odwołań do obiektów, które trzeba później zmodyfikować. Zobacz, jak prosty jest powyższy kod :) – jhabbott

+0

Tak, zaimplementowałem renderowanie tła za pomocą GCD, jest bardzo wygodne w użyciu i prawie kończę z tym samym wzorem co twoja odpowiedź, dziękuję Ci za użyteczne rozwiązanie! –

Powiązane problemy