iOS start Background Thread

117

Eu tenho um pequeno sqlitedb em meu dispositivo iOS. Quando um usuário pressiona um botão, eu busco os dados do sqlite e os mostro ao usuário.

Esta parte de busca eu quero fazer em um thread de segundo plano (para não bloquear o thread principal da IU). Eu faço isso assim -

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

Após a busca e um pouco de processamento, preciso atualizar a IU. Mas já que (como uma boa prática), não devemos realizar a atualização da IU a partir de threads em segundo plano. Eu chamo um selectorno mainthread assim -

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

Mas meu aplicativo trava na primeira etapa. ou seja, iniciando um tópico de fundo. Não é uma maneira de iniciar threads em segundo plano no iOS?

ATUALIZAÇÃO 1: depois de [self performSelectorInBackground....obter este rastreamento de pilha, nenhuma informação nunca -

insira a descrição da imagem aqui

ATUALIZAÇÃO 2: Eu até tentei, iniciando um thread de segundo plano assim - [NSThread detachNewThreadSelector:@selector(getResultSetFromDB:) toTarget:self withObject:docids];mas ainda obtenho o mesmo rastreamento de pilha.

Só para que eu esclareça, quando eu executo essa operação no thread principal tudo funciona bem ...

ATUALIZAÇÃO 3 Este é o método que estou tentando executar em segundo plano

- (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
fonte
Que log de erros / travamentos você obtém?
jtbandes de
Por favor, veja minhas atualizações ...
Srikar Appalaraju
Você pode mostrar o método que está chamando em segundo plano? E certifique-se de que o objeto docidsseja retido.
Rog
sim, docidssão retain. Eu coloquei .hcomo@property (nonatomic, retain) NSMutableArray *docids;
Srikar Appalaraju
Não prefixe métodos com get; isso deveria serresultSetFromDB:
bbum

Respostas:

270

Se você usar performSelectorInBackground:withObject:para gerar um novo thread, o seletor executado é responsável por configurar o pool de autorelease do novo thread, executar loop e outros detalhes de configuração - consulte "Usando NSObject para gerar um thread" no Guia de programação de threading da Apple .

Você provavelmente ficaria melhor usando o Grand Central Dispatch , no entanto:

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

GCD é uma tecnologia mais recente e mais eficiente em termos de sobrecarga de memória e linhas de código.


Atualizado com uma dica de chapéu para Chris Nolet , que sugeriu uma mudança que torna o código acima mais simples e acompanha os exemplos de código GCD mais recentes da Apple.

Scott Forbes
fonte
legal! não sabia disso. Isso se aplica a [NSThread detachNewThreadSelector:@selector....também?
Srikar Appalaraju
Sim. De acordo com os documentos da Apple, chamar performSelectorInBackground:withObject:"é o mesmo que chamar o detachNewThreadSelector:toTarget:withObject:método de NSThreadcom o objeto, seletor e objeto de parâmetro atuais como parâmetros."
Scott Forbes de
Existe uma diferença entre (unsigned long)NULLe 0neste assunto?
Sti,
4
@Sti da apple Dev Docs : Nota: O segundo argumento para a função dispatch_get_global_queue é reservado para expansão futura. Por enquanto, você deve sempre passar 0 para este argumento.
Jawad Al Shaikh
Devo então usar performSelectorOnMainThread para atualizar a IU com os resultados da operação ou há uma maneira mais consistente de atualizar a IU com GCD?
Ilya Denisov
9

Bem, isso é muito fácil na verdade com GCD. Um fluxo de trabalho típico seria algo assim:

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

Para saber mais sobre GCD, você pode dar uma olhada na documentação da Apple aqui

Pawan Ahire
fonte
4

Habilite NSZombieEnabled para saber qual objeto está sendo liberado e então acessado. Em seguida, verifique se o getResultSetFromDB:tem algo a ver com isso. Verifique também se docidshá algo dentro e se está sendo retido.

Dessa forma, você pode ter certeza de que não há nada errado.

Nicolas S
fonte
Copie a linha que você usou e que funcionou perfeitamente no tópico principal.
Nicolas S de
Eu uso isso do thread principal e pelo menos atinge esse método em vez de travar abruptamente -[self getResultSetFromDB:docids];
Srikar Appalaraju
você habilitou o que eu te disse?
Nicolas S de
Coloque um ponto de interrupção nesta linha: SpotMain * mirror = [[SpotMain alloc] init]; e me diga se bateu e, se tehn, qual linha trava. Habilite os zumbis para que possamos obter um registro de erros claro.
Nicolas S de
sim, habilitei zumbis. Eu entendi - `2011-08-14 12: 49: 42.697 FLO [16211: 707] *** - [FMResultSet release]: mensagem enviada para a instância desalocada 0x2bff80 2011-08-14 12: 49: 42.697 FLO [16211: 1607] *** __NSAutoreleaseNoPool (): Objeto 0x2c0cc0 da classe __NSCFData autoreleased sem pool no lugar - apenas vazando . Also when I try to call this method from background thread I do not reach SpotMain * mirror ... `, Ele trava logo após entrar no thread de background ...
Srikar Appalaraju
2

A biblioteca sqlite padrão que vem com o iOS não é compilada usando a macro SQLITE_THREADSAFE. Este pode ser o motivo pelo qual seu código falha.

Mugunth
fonte
2

Resposta do Swift 2.x:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        self.getResultSetFromDB(docids)
    }
Crashalot
fonte