Question

Je ne parviens pas à trouver une bonne documentation sur la façon de sous-classe NSOperation être simultanée et également à l'annulation de soutien. J'ai lu les documents d'Apple, mais je ne peux pas trouver un exemple « officiel ».

Voici mon code source:

@synthesize isExecuting = _isExecuting;
@synthesize isFinished = _isFinished;
@synthesize isCancelled = _isCancelled;

- (BOOL)isConcurrent
{
    return YES;
}

- (void)start
{
/* WHY SHOULD I PUT THIS ?
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }
*/

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];


    if (_isCancelled == YES)
    {
        NSLog(@"** OPERATION CANCELED **");
    }
    else
    {
        NSLog(@"Operation started.");
        sleep(1);
        [self finish];
    }
}

- (void)finish
{
    NSLog(@"operationfinished.");

    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];

    _isExecuting = NO;
    _isFinished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];

    if (_isCancelled == YES)
    {
        NSLog(@"** OPERATION CANCELED **");
    }
}

Dans l'exemple que je trouve, je ne comprends pas pourquoi performSelectorOnMainThread: est utilisé. Il empêcherait mon opération de fonctionner simultanément.

En outre, quand je commente sur cette ligne, je reçois mes opérations en cours d'exécution en même temps. Cependant, le drapeau de isCancelled n'est pas modifié, même si je l'ai appelé cancelAllOperations.

Était-ce utile?

La solution

D'accord, donc si je comprends bien, vous avez deux questions:

  1. Avez-vous besoin du segment performSelectorOnMainThread: qui apparaît dans les commentaires dans votre code? Qu'est-ce que le code fait?

  2. Pourquoi le drapeau _isCancelled n'est pas modifié lorsque vous appelez cancelAllOperations sur le NSOperationQueue qui contient cette opération?

deal Let avec ces dans l'ordre. Je vais supposer que votre sous-classe de NSOperation est appelé MyOperation, juste pour faciliter l'explication. Je vais vous expliquer ce que vous êtes malentendu et donner un exemple corrigé.

1. En cours d'exécution en même temps NSOperations

La plupart du temps, vous utiliserez NSOperations avec un NSOperationQueue et de votre code, il semble que c'est ce que vous faites. Dans ce cas, votre MyOperation sera toujours exécuté sur un fil d'arrière-plan, quelle que soit la méthode retourne de -(BOOL)isConcurrent, puisque NSOperationQueues sont explicitement conçues pour les opérations d'exécution en arrière-plan.

En tant que tel, vous n'avez pas besoin généralement de passer outre la méthode de -[NSOperation start], puisque par défaut, il invoque simplement la méthode -main. C'est la méthode que vous devriez être prépondérant. La méthode -start par défaut gère déjà la mise en isExecuting et isFinished pour vous au moment opportun.

Donc, si vous voulez un NSOperation courir en arrière-plan, remplacer simplement la méthode de -main et de le mettre sur un NSOperationQueue.

Le performSelectorOnMainThread: dans votre code causerait tous les cas de MyOperation d'accomplir toujours sa tâche sur le thread principal. Comme un seul morceau de code peut être exécuté sur un fil à la fois, cela signifie qu'aucun autre MyOperations pourrait être en cours d'exécution. Tout le but de NSOperation et NSOperationQueue est de faire quelque chose en arrière-plan.

La seule fois où vous voulez forcer les choses sur le thread principal est lorsque vous mettez à jour l'interface utilisateur. Si vous avez besoin de mettre à jour l'interface utilisateur lorsque la fin de votre MyOperation, que est quand vous devez utiliser performSelectorOnMainThread:. Je vais vous montrer comment faire dans mon exemple ci-dessous.

2. Annulation d'une NSOperation

-[NSOperationQueue cancelAllOperations] appelle la méthode -[NSOperation cancel], ce qui provoque des appels ultérieurs à -[NSOperation isCancelled] à YES retour. Cependant , vous avez fait deux choses pour faire de cette inefficacité.

  1. Vous utilisez @synthesize isCancelled pour remplacer la méthode de -isCancelled de NSOperation. Il n'y a aucune raison de le faire. NSOperation déjà des outils -isCancelled d'une manière tout à fait acceptable.

  2. Vous regardez votre propre variable d'instance de _isCancelled pour déterminer si l'opération a été annulée. NSOperation garantit que [self isCancelled] retournera YES si l'opération a été annulée. Finalité pas garantie que votre méthode setter personnalisée sera appelée, ni que votre propre variable d'instance est à jour. Vous devriez vérifier [self isCancelled]

Qu'est-ce que vous devez faire

L'en-tête:

// MyOperation.h
@interface MyOperation : NSOperation {
}
@end

Et la mise en œuvre:

// MyOperation.m
@implementation MyOperation

- (void)main {
    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }

    // Do some work here
    NSLog(@"Working... working....")

    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }
    // Do any clean-up work here...

    // If you need to update some UI when the operation is complete, do this:
    [self performSelectorOnMainThread:@selector(updateButton) withObject:nil waitUntilDone:NO];

    NSLog(@"Operation finished");
}

- (void)updateButton {
    // Update the button here
}
@end

Notez que vous n'avez pas besoin de faire quoi que ce soit avec isExecuting, isCancelled ou isFinished. Ceux-ci sont tous traités automatiquement pour vous. Il suffit de remplacer la méthode de -main. Il est facile.

(Une note:. Techniquement, ce n'est pas une NSOperation « concurrente », au sens où -[MyOperation isConcurrent] retournerait NO mis en œuvre ci-dessus Cependant, être exécuté sur un thread d'arrière-plan Le isConcurrent. méthode doit vraiment être nommé -willCreateOwnThread, car c'est une description plus précise de l'intention de la méthode.)

Autres conseils

L'excellente réponse @BJHomer mérite une mise à jour.

opérations simultanées doivent remplacer la méthode de start au lieu de main.

Comme indiqué dans le d'Apple Documentation :

Si vous créez une opération simultanée, vous devez remplacer les méthodes suivantes et propriétés au minimum:

  • start
  • asynchronous
  • executing
  • finished

Une mise en œuvre également nécessite pour remplacer cancel aussi bien. Faire une sous-classe thread-safe et obtenir le droit sémantique requis est également tout à fait délicate.

Ainsi, je l'ai mis une sous-classe complète et travaille comme proposition mise en œuvre à Swift dans la révision du code. Commentaires et suggestions sont les bienvenus.

Cette classe peut être facilement utilisé comme une classe de base pour votre classe opération personnalisée.

Je sais que c'est une vieille question, mais je l'ai enquêté sur ces derniers temps et ce a rencontré les mêmes exemples et avait les mêmes doutes.

Si votre travail peut être exécuté de manière synchrone dans la principale méthode, vous n'avez pas besoin d'une opération concurrente, ni début primordial, il suffit de faire votre travail et retour principal lorsque vous avez terminé.

Cependant, si votre charge de travail est asynchrone par nature - i.e. le chargement d'un NSURLConnection, vous devez commencer sous-classe. Lorsque votre retour de la méthode de démarrage, l'opération n'est pas encore terminée. Il ne sera considéré comme terminé par le NSOperationQueue lorsque vous envoyez manuellement les notifications KVO aux drapeaux isFinished et isExecuting (par exemple, une fois que les finitions de chargement d'URL ou de synchronisation tombe en panne).

Enfin, on peut vouloir expédier début du thread principal lorsque la charge de travail async vous voulez commencer à exiger une écoute en boucle d'exécution sur le thread principal. Comme le travail lui-même est asynchrone, il ne sera pas limiter votre concurrence, mais à partir du travail dans un thread de travail pourrait ne pas avoir un bon runloop prêt.

Jetez un oeil à ASIHTTPRequest . Il est un HTTP wrapper classe construite au-dessus de NSOperation comme une sous-classe et semble mettre en œuvre. Notez que la mi-2011, le développeur recommande de ne pas utiliser l'ASI pour les nouveaux projets.

En ce qui concerne définir " annulé " propriété (ou définir " _cancelled " Ivar) à l'intérieur NSOperation sous-classe, qui est normalement pas nécessaire. Tout simplement parce que lorsque USER déclenche l'annulation, le code personnalisé doit toujours informer les observateurs KVO que votre opération est maintenant terminé avec son travail. En d'autres termes, isCancelled => isFinished.

En particulier, lorsque l'objet NSOperation dépend de la réalisation d'autres objets d'exploitation, il surveille le chemin de clé isFinished pour ces objets. A défaut de générer une notification d'arrivée ( en cas d'annulation arrive ) peut donc empêcher l'exécution d'autres opérations dans votre application.


BTW, la réponse de @BJ Homer: "La méthode isConcurrent devrait vraiment être nom -willCreateOwnThread" logique BEAUCOUP !

Parce que si vous ne remplacez pas méthode commencer, il suffit d'appeler manuellement de NSOperation-objet par défaut-démarrage méthode, qui se fait appeler-fil est, par défaut, synchrone; donc, NSOperation-objet est seulement une opération non simultanée.

Cependant, si vous démarrage méthode de remplacement, à l'intérieur la mise en oeuvre méthode début, code personnalisé devrait frayer un thread séparé ... etc, alors vous cassez avec succès la restriction de « appelant fil étant par défaut synchrone », ce qui rend donc NSOperation-objet devient une opération concurrente, il peut fonctionner de manière asynchrone par la suite.

Ce billet de blog:

http://www.dribin.org/dave / blog / Archives / 2009/09/13 / snowy_concurrent_operations /

explique pourquoi vous pourriez avoir besoin:

if (![NSThread isMainThread])
{
    [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
    return;
}

dans votre méthode start.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top