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

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, docidssą retain. 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.


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


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.


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.


2

Odpowiedź Swift 2.x:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        self.getResultSetFromDB(docids)
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.