Uruchom wątek w tle w systemie iOS

117

Mam mały sqlitedb na moim urządzeniu z iOS. Kiedy użytkownik naciska przycisk, pobieram dane z sqlite i pokazuję je użytkownikowi.

Ta część pobierania chcę to zrobić w wątku w tle (aby nie blokować głównego wątku interfejsu użytkownika). Robię to tak -

[self performSelectorInBackground:@selector(getResultSetFromDB:) withObject:docids];

Po pobraniu i trochę przetworzeniu muszę zaktualizować interfejs użytkownika. Ale ponieważ (jako dobra praktyka) nie powinniśmy przeprowadzać aktualizacji interfejsu użytkownika z wątków w tle. Wzywam takiego selectorgłównego wątku -

[self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];

Ale moja aplikacja ulega awarii w pierwszym kroku. tj. rozpoczęcie wątku w tle. Czy to nie jest sposób na uruchamianie wątków w tle w iOS?

AKTUALIZACJA 1: Po tym, [self performSelectorInBackground....jak otrzymam ten ślad stosu, nie ma żadnych informacji -

wprowadź opis obrazu tutaj

AKTUALIZACJA 2: Próbowałem nawet rozpocząć wątek w tle w ten sposób - [NSThread detachNewThreadSelector:@selector(getResultSetFromDB:) toTarget:self withObject:docids];ale nadal otrzymuję ten sam ślad stosu.

Tylko dla wyjaśnienia, kiedy wykonuję tę operację na głównym wątku, wszystko działa gładko ...

UPDATE 3 To jest metoda, którą próbuję uruchomić w tle

- (void)getResultSetFromDB:(NSMutableArray *)toProceessDocids
{
    SpotMain *mirror = [[SpotMain alloc] init];
    NSMutableArray *filteredDocids = toProceessDocids;

    if(![gMediaBucket isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaBucketWithDocID:filteredDocids mBucket:gMediaBucket numRes:-1];
    if(![gMediaType isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaType:filteredDocids mediaType:gMediaType numRes:-1];
    if(![gPlatform isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForPlatformID:filteredDocids platformId:@"1" numRes:-1];

    self.resultSet = [mirror FetchObjectFromDocid:filteredDocids];
    [filteredDocids release];
    [mirror release];

    [self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];
    return;
}
Srikar Appalaraju
źródło
Jaki dziennik błędów / awarii otrzymujesz?
jtbandes
Proszę zobaczyć moje aktualizacje ...
Srikar Appalaraju
Czy możesz pokazać w tle metodę, którą wywołujesz? I upewnij się, że obiekt docidsjest zachowany.
Rog,
tak, docidsretain. Umieściłem to .hjako@property (nonatomic, retain) NSMutableArray *docids;
Srikar Appalaraju
Nie poprzedzaj metod get; tak powinno byćresultSetFromDB:
bbum

Odpowiedzi:

270

Jeśli używasz performSelectorInBackground:withObject:do tworzenia nowego wątku, to wykonany selektor jest odpowiedzialny za ustawienie puli autorelease nowego wątku, pętli uruchamiania i innych szczegółów konfiguracyjnych - zobacz „Używanie NSObject do tworzenia wątku” w Przewodniku programowania wątków Apple .

Prawdopodobnie lepiej byłoby, gdybyś skorzystał z Grand Central Dispatch :

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self getResultSetFromDB:docids];
});

GCD jest nowszą technologią i jest bardziej wydajna pod względem narzutu pamięci i wierszy kodu.


Zaktualizowano wskazówką dotyczącą kapelusza dla Chrisa Noleta , który zasugerował zmianę, która uprości powyższy kod i dotrzymuje kroku najnowszym przykładom kodu GCD firmy Apple.

Scott Forbes
źródło
chłodny! nie wiedziałem o tym. Czy dotyczy to [NSThread detachNewThreadSelector:@selector....również?
Srikar Appalaraju
Tak. Zgodnie z dokumentacją Apple wywołanie performSelectorInBackground:withObject:„jest takie samo, jak wywołanie detachNewThreadSelector:toTarget:withObject:metody NSThreadz bieżącym obiektem, selektorem i obiektem parametrów jako parametrami”.
Scott Forbes,
Czy jest jakaś różnica między (unsigned long)NULLiw 0tej kwestii?
Sti
4
@Sti z Apple Dev Docs : Uwaga: Drugi argument funkcji dispatch_get_global_queue jest zarezerwowany do przyszłej rozbudowy. Na razie należy zawsze podawać 0 dla tego argumentu.
Jawad Al Shaikh
Czy powinienem wtedy użyć performSelectorOnMainThread do zaktualizowania interfejsu użytkownika za pomocą wyników operacji, czy jest bardziej spójny sposób aktualizacji interfejsu użytkownika za pomocą GCD?
Ilya Denisov
9

Cóż, jest to całkiem proste w przypadku GCD. Typowy przepływ pracy wyglądałby tak:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        // Perform async operation
        // Call your method/function here
        // Example:
        // NSString *result = [anObject calculateSomething];
                dispatch_sync(dispatch_get_main_queue(), ^{
                    // Update UI
                    // Example:
                    // self.myLabel.text = result;
                });
    });

Więcej informacji na temat GCD można znaleźć w dokumentacji firmy Apple tutaj

Pawan Ahire
źródło
4

Włącz NSZombieEnabled, aby wiedzieć, który obiekt jest zwalniany, a następnie dostępny. Następnie sprawdź, czy getResultSetFromDB:ma z tym coś wspólnego. Sprawdź również, czy docidscoś jest w środku i czy jest zachowane.

W ten sposób możesz mieć pewność, że wszystko jest w porządku.

Nicolas S.
źródło
Skopiuj używaną linię, która działa płynnie w głównym wątku.
Nicolas S
Używam tego z głównego wątku i przynajmniej trafia w tę metodę zamiast nagle się zawieszać -[self getResultSetFromDB:docids];
Srikar Appalaraju
czy włączyłeś to, co ci powiedziałem?
Nicolas S
Umieść punkt przerwania w tym wierszu: SpotMain * mirror = [[SpotMain assign] init]; i powiedz mi, czy to trafienie, a jeśli to, która linia się zawiesza. Włącz zombie, proszę, abyśmy mogli uzyskać przejrzysty dziennik błędów.
Nicolas S
tak, włączyłem zombie. Otrzymuję to - `2011-08-14 12: 49: 42.697 FLO [16211: 707] *** - [wersja FMResultSet]: wiadomość wysłana do cofniętej instancji 0x2bff80 2011-08-14 12: 49: 42.697 FLO [16211: 1607] *** __NSAutoreleaseNoPool (): Object 0x2c0cc0 klasy __NSCFData autoreleased bez basenu w miejscu - tylko przecieka . Also when I try to call this method from background thread I do not reach SpotMain * lustro ... `, zawiesza się zaraz po wejściu na wątek tła ...
Srikar Appalaraju
2

Domyślna biblioteka sqlite dostarczana z systemem iOS nie jest kompilowana przy użyciu makra SQLITE_THREADSAFE na. Może to być powód awarii kodu.

Mugunth
źródło
2

Odpowiedź Swift 2.x:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        self.getResultSetFromDB(docids)
    }
Crashalot
źródło