2013-06-27 24 views
7

W mojej aplikacji mam kontroler UITableViewController.Załaduj ponownie dane UITableView w tle

Jego tableView jest podzielony na 3 sekcje.

Pobieram dane do każdej z tych sekcji z mojego serwera. Aby to zrobić, mam 3 funkcje (na przykład f1 f2 i f3). Każdy aktualizuje odpowiednią NSArray, używaną jako źródło danych dla mojej tabeli.

Teraz chcę przeładować dane za pomocą tych funkcji i odświeżyć mój tableView po wykonaniu tych 3 funkcji, ale bez przeszkadzania użytkownikowi.

Nie jestem używany z asynchronicznym żądaniem, blokami, wątkami itp. ... i szukam wskazówek.

Faktycznie, tutaj jest to, co robię:

-(void)viewDidLoad 
{ 
    //some settings 

    [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:@selector(reloadDatas) userInfo:nil repeats:YES]; 

    dispatch_queue_t queue = dispatch_get_main_queue(); 
    dispatch_async(queue, ^{ 
     [self reloadDatas]; 
    }); 
} 

-(void)reloadDatas 
{ 
    dispatch_queue_t concurrentQueue = dispatch_get_main_queue(); 
    dispatch_async(concurrentQueue, ^{ 
     [self f1]; 
     [self f2]; 
     [self f3]; 
     [myDisplayedTable reloadData]; 
    }); 
} 

-(void)f1 
{ 
    //load datas with a url request and update array1 
} 
-(void)f2 
{ 
    //load datas with a url request and update array2 
} 
-(void)f3 
{ 
    //load datas with a url request and update array3 
} 

Ale tutaj, mój tableView jest "zamrożony", dopóki nie jest odświeżany.

Nie obchodzi mnie kolejność wykonywania f1 f2 i f3, ale muszę poczekać, aż te 3 funkcje zostaną wykonane przed odświeżeniem mojej tableView.

Dzięki za pomoc.

EDIT

Dzięki za wszystkie odpowiedzi.

Oto roztwór roboczy:

Jak MRO suggets, usunąłem kolejkę wysyłać z viewDidLoad i zastąpić w reloadDatas:

dispatch_queue_t concurrentQueue = dispatch_get_main_queue(); 

z

dispatch_queue_t mainThreadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

I wreszcie, Odświeżam mój stół w głównym wątku

dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; }); 

Odpowiedz

4

Więc twój "wątek tła" jest w rzeczywistości twoim głównym wątkiem. Musisz użyć metody dispatch_get_global_queue i określić priorytet, aby uzyskać inny wątek. Również asynchronizacja wysyłki w viewDidLoad jest bezużyteczna, ponieważ wszystkie metody cyklu życia kontrolera widoku są wywoływane w głównym wątku. Zalecam wykonanie następujących czynności w metodach f1, f2 i f3:

Rozpocznij od uruchomienia asynchronicznego żądania adresu URL, następnie w bloku zakończenia zaktualizuj tablicę X i załaduj ponownie sekcję widoku tabeli. W ten sposób wszystkie trzy żądania mogą odbywać się jednocześnie, a tabela aktualizuje niezbędne dane po zakończeniu każdego z nich.Jeśli chcesz przeładować tylko raz, po prostu zamień zmienną concurrentQueue na wątek tła, a następnie wykonaj [tableView reloadData] w wątku głównym.

2

W reloadDatas metody należy zmienić tę linię:

dispatch_queue_t concurrentQueue = dispatch_get_main_queue(); 

Do:

dispatch_queue_t concurrentQueue = dispatch_queue_create("some queue", NULL);

Ale kiedy zadzwonić [myDisplayedTable reloadData], trzeba nazwać tę operację w głównej kolejki.

dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; }); 
+0

Y czy utworzyłeś współbieżne miejsce? – abhi1992

3

Poprzednie odpowiedzi są absolutnie poprawne. Jednak twoja implementacja reloadDatas & viewDidLoad jest nieco problematyczna.

prostu do wyjaśnienia:

Chcesz zakończyć czasochłonne ładowanie danych rzeczy w wątku tła, a następnie zaktualizować UI/komórek, gdy dane jest gotowy na głównym wątku.

tak:

-(void)viewDidLoad 
    { 
     dispatch_queue_t concurrentQueue = dispatch_queue_create("com.my.backgroundQueue", NULL); 
     dispatch_async(concurrentQueue, ^{ 
      [self reloadDatas]; 
     }); 
    } 

-(void)reloadDatas 
{ 
    // Expensive operations i.e pull data from server and add it to NSArray or NSDictionary 
     [self f1]; 
     [self f2]; 
     [self f3]; 

    // Operation done - now let's update our table cells on the main thread 

    dispatch_queue_t mainThreadQueue = dispatch_get_main_queue(); 
    dispatch_async(mainThreadQueue, ^{ 

     [myDisplayedTable reloadData]; // Update table UI 
    }); 
} 
+0

Próbowałem Twojego rozwiązania, ale interakcje użytkownika i ponowne ładowanie blokują się nawzajem. Mam na myśli, że reloadDatas nie jest wywoływany, ponieważ użytkownik nie zatrzymał przewijania tabeli, a użytkownik nie może przewinąć tabeli, gdy tabela nie jest odświeżana ... – zbMax

+0

Czy na pewno nie jest wywoływana, ponieważ użytkownik przewija tabelę ? Spróbuj dodać kilka NSLogów lub punktów przerwania. – smaura777

+0

tak, to właśnie mam na myśli przez "interakcje użytkownika": podczas przewijania tabeli mój NSTimer nie wywołuje mojej funkcji do momentu zakończenia przewijania. – zbMax

1

jedno. Pobieranie danych z serwera i aktualizowanie komórek tabeli jest dość powszechne. Tutaj nie ma potrzeby wprowadzania kolejek ani minutników. Oto alternatywna struktura.

Załóżmy, że ciągnięcie mp3 z serwera: Twoja klasa model: Music.h/m Menedżer modelu jest: MusicManager.h/m (Singleton) - będzie zawierać tablicę obiektów muzycznych - że singleton jest w zasadzie twoim źródłem danych; i wreszcie swoją UITableViewController: MusicTableVC.h/m

W MusicManager.h/m: masz NSMutableArray które zostaną załadowane przedmiotami Music.h że już wyciągnąć z serwera. Możesz to zrobić zaraz po załadowaniu aplikacji, nawet nie czekając na TableViewController.

Wewnątrz MusicManager masz kilka metod pomocniczych, aby dodać lub usunąć elementy z mutableArray i podać liczbę i oczywiście swoje metody sieciowe.

Wreszcie: opublikuj powiadomienie w kodzie sieciowym. Twój kontroler UITableViewController powinien słuchać/obserwować to powiadomienie i odpowiednio "ładować".

[[NSNotificationCenter defaultCenter] postNotificationName:@"NewMusicAdded" object:nil]; 

Ty kwerendy danych z serwera, przetwarza dane w obiekty muzyczne dodać je do NSMutable tablicy i umieścić zawiadomienie pozwoli zaktualizować tabela sama.

Bardzo standardowy przepis.

Powiązane problemy