118 votes

iOS lance le fil de fond

J'ai un petit sqlitedb dans mon appareil iOS. Lorsqu'un utilisateur appuie sur un bouton, je récupère les données de sqlite et les affiche à l'utilisateur.

Cette partie de récupération, je veux la faire dans un thread d'arrière-plan (pour ne pas bloquer le thread principal de l'interface utilisateur). Je fais cela comme suit

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

Après la récupération et un peu de traitement, je dois mettre à jour l'interface utilisateur. Mais comme (en tant que bonne pratique) nous ne devrions pas effectuer la mise à jour de l'interface utilisateur à partir de threads d'arrière-plan. J'appelle un selector sur le fil principal comme ça -

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

Mais mon application se plante à la première étape, c'est-à-dire au démarrage d'un fil d'arrière-plan. N'y a-t-il pas un moyen de lancer des threads en arrière-plan dans iOS ?

UPDATE 1 : Après [self performSelectorInBackground.... Je reçois cette trace de pile, sans aucune information.

enter image description here

UPDATE 2 : J'ai même essayé, en commençant un fil de fond comme ceci - [NSThread detachNewThreadSelector:@selector(getResultSetFromDB:) toTarget:self withObject:docids]; mais je reçois toujours la même trace de pile.

Pour que ce soit clair, lorsque j'effectue cette opération sur le fil principal, tout se passe bien...

MISE À JOUR 3 Voici la méthode que j'essaie d'exécuter à partir de l'arrière-plan

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

0 votes

Quel journal d'erreurs ou de pannes obtenez-vous ?

0 votes

Veuillez voir mes mises à jour...

0 votes

Pouvez-vous montrer la méthode que vous appelez en arrière-plan ? Et assurez-vous que l'objet docids est conservé.

272voto

Scott Forbes Points 5567

Si vous utilisez performSelectorInBackground:withObject: pour créer un nouveau thread, le sélecteur exécuté est responsable de la mise en place du pool autorelease du nouveau thread, de la boucle d'exécution et d'autres détails de configuration - cf. "Utiliser NSObject pour créer un fil de discussion" dans le site d'Apple Guide de programmation du filetage .

Vous feriez probablement mieux d'utiliser Grand Central Dispatch mais.. :

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

Le GCD est une technologie plus récente, plus efficace en termes d'utilisation de la mémoire et de lignes de code.


Mise à jour de avec un coup de chapeau à Chris Nolet qui a suggéré un changement qui rend le code ci-dessus plus simple et suit les derniers exemples de code GCD d'Apple.

0 votes

Cool ! Je ne le savais pas. Est-ce que cela s'applique à [NSThread detachNewThreadSelector:@selector.... aussi ?

0 votes

Oui. Selon la documentation d'Apple, appeler performSelectorInBackground:withObject: ", c'est la même chose que si vous appeliez le detachNewThreadSelector:toTarget:withObject: méthode de NSThread avec l'objet courant, le sélecteur et l'objet paramètre comme paramètres."

0 votes

Y a-t-il une différence entre (unsigned long)NULL et 0 dans cette affaire ?

11voto

Pawan Ahire Points 179

C'est assez facile en fait avec GCD. Un flux de travail typique serait quelque chose comme ceci :

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

Pour en savoir plus sur le GCD, vous pouvez consulter La documentation d'Apple ici

4voto

Nicolas S Points 2495

Activer NSZombieEnabled pour savoir quel objet est libéré et ensuite accédé. Vérifiez ensuite si le getResultSetFromDB: a quelque chose à voir avec ça. Vérifiez également si docids a quelque chose à l'intérieur et s'il est conservé.

De cette façon, vous pouvez être sûr qu'il n'y a pas de problème.

0 votes

Veuillez copier la ligne que vous avez utilisée et qui fonctionne bien sur le fil principal.

0 votes

J'utilise cette méthode à partir du fil de discussion principal et au moins elle est utilisée au lieu de s'arrêter brusquement. [self getResultSetFromDB:docids];

0 votes

Avez-vous activé ce que je vous ai dit ?

2voto

Mugunth Points 8671

La bibliothèque sqlite par défaut fournie avec iOS n'est pas compilée avec la macro SQLITE_THREADSAFE activée. Cela pourrait être la raison pour laquelle votre code se bloque.

2voto

Crashalot Points 3805

Réponse à Swift 2.x :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X