NSOperationQueue finie, comment le savoir ?

Salut,



J'utilise dans une appli une NSOperationQueue, sur deux opération AFNetworking.



Comme j'affiche une fenêtre de chargement, j'ai besoin de savoir quand les opérations de la "queue" sont terminées pour faire le dismiss, mais je ne vois pas comment faire. J'ai tatonné avec un addObserver mais sans succès.



Pour la queue, j'utilise juste ce code :






<br />
    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];<br />
    [queue addOperation:teamOperation];<br />
    [queue addOperation:champOperation];<br />




Merci de votre aide image/smile.png' class='bbc_emoticon' alt=':)' />

Réponses

  • psychoh13psychoh13 Mothership Developer Membre
    Tu peux observer à  l'aide de KVO operationCount qui est mis à  jour lorsque les opérations se terminent.
  • J'ai réussi en faisant ça :




    [queue addObserver:self forKeyPath:@&quot;operations&quot; options:0 context:NULL];
    




    et




    <br />
    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object<br />
                             change:(NSDictionary *)change context:(void *)context<br />
    {<br />
        if (object == self.queue &amp;&amp; [keyPath isEqualToString:@&quot;operations&quot;]) {<br />
            if ([self.queue.operations count] == 0) {<br />
                [self displayMSG:[NSDictionary dictionaryWithObjectsAndKeys:@&quot;Mise à  jour effectuée&quot;, @&quot;status&quot;, nil]];<br />
            }<br />
        }<br />
        else {<br />
            [super observeValueForKeyPath:keyPath ofObject:object<br />
                                   change:change context:context];<br />
        }<br />
    }<br />
    




    Mais je ne sais pas si c'est la meilleure méthode image/smile.png' class='bbc_emoticon' alt=':)' />
  • psychoh13psychoh13 Mothership Developer Membre
    'Steph' a écrit:


    J'ai réussi en faisant ça :




    [queue addObserver:self forKeyPath:@&quot;operations&quot; options:0 context:NULL];
    




    et




    <br />
    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object<br />
    						 change:(NSDictionary *)change context:(void *)context<br />
    {<br />
    	if (object == self.queue &amp;&amp; [keyPath isEqualToString:@&quot;operations&quot;]) {<br />
    		if ([self.queue.operations count] == 0) {<br />
    			[self displayMSG:[NSDictionary dictionaryWithObjectsAndKeys:@&quot;Mise à  jour effectuée&quot;, @&quot;status&quot;, nil]];<br />
    		}<br />
    	}<br />
    	else {<br />
    		[super observeValueForKeyPath:keyPath ofObject:object<br />
    							   change:change context:context];<br />
    	}<br />
    }<br />
    




    Mais je ne sais pas si c'est la meilleure méthode image/smile.png' class='bbc_emoticon' alt=':)' />




    Tu devrais remplacer @operations avec @operationCount, aussi il faut que tu passes un context:


    <br />
    <br />
    [font=Menlo]<br />
    [color=#0433ff]static[/color] [color=#0433ff]void[/color] *[color=#0433ff]const[/color] _MyOperationCountObservingContext = ([color=#0433ff]void[/color] *)&amp;[color=#985546]_MyOperationCountObservingContext[/color];[/font]<br />
    [font=Menlo]<br />
    - ([color=#0433ff]void[/color])someMethod;[/font][font=Menlo]<br />
    {[/font][color=#985546][font=Menlo]<br />
    [color=#000000]    [[[/color][color=#0433ff]self[/color][color=#000000] [/color]queue[color=#000000]] [/color][color=#0c8a68]addObserver[/color][color=#000000]:[/color][color=#0433ff]self[/color][color=#000000] [/color][color=#0c8a68]forKeyPath[/color][color=#000000]:[/color][color=#797baa]@&quot;operationCount&quot;[/color][color=#000000] [/color][color=#0c8a68]options[/color][color=#000000]:[/color][color=#797baa]0[/color][color=#000000] [/color][color=#0c8a68]context[/color][color=#000000]:[/color]_MyOperationCountObservingContext[color=#000000]];[/color][/font][/color][font=Menlo]<br />
    }[/font]<br />
    [font=Menlo]<br />
    - ([color=#0433ff]void[/color])removeQueue[/font][font=Menlo]<br />
    {[/font][color=#985546][font=Menlo]<br />
    [color=#000000]    [[[/color][color=#0433ff]self[/color][color=#000000] [/color]queue[color=#000000]] [/color][color=#0c8a68]removeObserver[/color][color=#000000]:[/color][color=#0433ff]self[/color][color=#000000] [/color][color=#0c8a68]forKeyPath[/color][color=#000000]:[/color][color=#797baa]@&quot;operationCount&quot;[/color][color=#000000] [/color][color=#0c8a68]context[/color][color=#000000]:[/color]_MyOperationCountObservingContext[color=#000000]];[/color][/font][/color][font=Menlo]<br />
    }[/font]<br />
    [font=Menlo]<br />
    - ([color=#0433ff]void[/color]) observeValueForKeyPath:([color=#0c8a68]NSString[/color] *)keyPath ofObject:([color=#0433ff]id[/color])object[/font][font=Menlo]<br />
                             change:([color=#0c8a68]NSDictionary[/color] *)change context:([color=#0433ff]void[/color] *)context[/font][font=Menlo]<br />
    {[/font][color=#985546][font=Menlo]<br />
    [color=#000000]    [/color][color=#0433ff]if[/color][color=#000000] (context == [/color]_MyOperationCountObservingContext[color=#000000])[/color][/font][/color][font=Menlo]<br />
        {[/font][font=Menlo]<br />
            [color=#0433ff]if[/color] ([[[color=#0433ff]self[/color] [color=#985546]queue[/color]] [color=#0c8a68]operationCount[/color]] == [color=#797baa]0[/color]) {[/font][color=#12A500][font=Menlo]<br />
    [color=#000000]            [/color]// All operations have finished[/font][/color][font=Menlo]<br />
            }[/font][font=Menlo]<br />
        }[/font][font=Menlo]<br />
        [color=#0433ff]else[/color] {[/font][color=#0C8A68][font=Menlo]<br />
    [color=#000000]        [[/color][color=#0433ff]super[/color][color=#000000] [/color]observeValueForKeyPath[color=#000000]:keyPath [/color]ofObject[color=#000000]:object[/color][/font][/color][font=Menlo]<br />
                                   [color=#0c8a68]change[/color]:change [color=#0c8a68]context[/color]:context];[/font][font=Menlo]<br />
        }[/font][font=Menlo]<br />
    }[/font]<br />
    <br />
    




    Il est important que le contexte soit unique et donc utiliser l'adresse d'une variable static est la meilleure façon d'avoir une valeur unique. Tu peux, par mesure de sécurité, vérifier que object == self.queue aussi.
  • En fait le truc c'est que j'ai l'impression que ça me retourne bien qu'il n'y a plus d'opération dans la file, mais par contre, celle-ci ne sont pas terminées, et j'ai besoin qu'elles soient terminées pour lancer mon traitement suivant image/sad.png' class='bbc_emoticon' alt=':(' />
  • AliGatorAliGator Membre, Modérateur
    Je n'ai pas souvent manipulé des NSOperationQueue, mais ne peux-tu pas créer une NSOperation correspondant à  ce que tu veux exécuter à  la fin (une fois que toutes les autres opérations sont finies), rajouter des dépendances entre chaque opération et cette opération finale, et ainsi elle ne s'exécutera que lorsque chacune de ses dépendances aura fini de s'exécuter, et donc seulement à  la fin ?
  • J'ai essayé mais j'ai des comportements bizarres.



    En fait, pendant mes opérations, je vais chercher un fichier JSON avec AFNetworking, une fois que j'ai le fichier, je test si je dois mettre à  jour ma base coredata et si oui j'exécute une méthode qui enregistre les données. Je viens de m'apercevoir que par exemple, quand ma méthode d'enregistrement est appelée je ne reçois jamais la notification de fin de queue.



    Je m'en suis apercu car quand mon operation count est à  0 je lance un NSNotification.



    Il doit y a voir un problème entre le main thread et le thread de ma notif ou truc du genre.



    Du coup, je réfléchi à  une autre conception pour voir.
  • StephSteph Membre
    juillet 2012 modifié #8
    Bon j'ai réussi avec une approche différente.



    Chaque opération AFNetwork est dans une méthode à  part et retourne un block.



    Dans une méthode globale, j'appelle les methodes qui démarrent les opérations en testant les blocks et en imbriquant celles-ci (quand la première a répondu, j'envoie la deuxième etc), sur la dernière, je place un NSNotification qui envoie à  ma vue principale que la mise à  jour est ok et qu'on peut passer à  la suite.



    J'ai diviser le temps de traitement par 5, ma vue attends les données pour les afficher et tout fonctionne à  merveille image/smile.png' class='bbc_emoticon' alt=':)' />



    Cherry on the cookie, pas un leak LOL



    Edit : et donc plus de NSOperationQueue image/tongue.png' class='bbc_emoticon' alt=':P' />
  • AliGatorAliGator Membre, Modérateur
    Heu ouais mais ça veut dire que tu attends que ta requête 1 soit finie pour envoyer ta requête 2, et que ta requête 2 soit finie pour envoyer ta requête 3, etc... avant d'envoyer ta notif finale... au lieu d'envoyer les 3 requêtes et d'envoyer ta notif quand les 3 requêtes sont terminées ?
  • En fait, les requêtes dépendent en partie les unes des autres, car j'ai besoin que les premiers éléments soient chargés pour balancer les suivants, donc dans le fonctionnement de mon appli ça colle image/smile.png' class='bbc_emoticon' alt=':)' />



    Je reconnais que s'il n'y avait pas de lien, ça serait pas le top !
  • Ce sujet m'intéresse fortement !

    J'aimerai lancer plusieurs requêtes asynchrones en même temps et pouvoir connaà®tre la fin de toutes les requêtes.

    Dois-je utiliser NSOperationQueue ?

    Y'a t-il un meilleur moyen d'arriver à  mes fins ?



    Par avance, merci.
  • Ya certainement un moyen mais avec NSOperationQueue je n'ai pas réussi.



    Je sais quand il ne reste plus de requêtes à  exécuter mais pas quand elles sont terminées, y compris avec un waitUntilFinished.
  • Personne aurait un moyen de savoir quand toutes les requêtes sont terminées en passant peut-être par d'autres méthodes ?



    Je continue mes recherches ..
  • AliGatorAliGator Membre, Modérateur
    Bah avec le "completion" block de chaque requête c'est le moyen dédié fourni par AFNetworking pour être averti quand une requête est terminée. Donc je pense qu'il faut partir de là .



    Par exemple prévoir un NSMutableSet* pendingRequests, mettre les requêtes / AFHTTPRequestOperations dedans avant de les lancer, dans le completion de chaque requête, enlever la requête correspondante du set pendingRequests, et regarder si pendingRequests.count est tombé à  zéro après ce remove.


    NSMutableSet* pendingRequests = [NSMutableSet set];<br />
        void (^checkAllDone)(AFHTTPRequestOperation*) = ^(AFHTTPRequestOperation* op)<br />
        {<br />
            [pendingRequests removeObject:op];<br />
            if (pendingRequests.count == 0)<br />
            {<br />
                NSLog(@&quot;All requests finished&#33;&quot;);<br />
            }<br />
        };<br />
    <br />
        AFHTTPRequestOperation* op1 = [[[AFHTTPRequestOperation alloc] initWithRequest:req1] autorelease];<br />
        [pendingRequests addObject:op1];<br />
        [op1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {<br />
            checkAllDone(operation);<br />
            // ...<br />
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {<br />
            checkAllDone(operation);<br />
            // ...<br />
        }];<br />
    // Et pareil pour les autres AFHTTPRequestOperations que l&#39;on veut surveiller, on les ajoute à  pendingRequest et on appelle checkAllDone dans leur completionblocks pour que ça vérifie si elles sont toutes finies<br />
    
    Bon ce n'est qu'une solution parmi d'autres, sans doute pas la seule possible.
  • Je ne peux pas utiliser AFNetworking mais tu m'as quand même mis sur le bon chemin.

    Enfin je l'espère. En tout cas, merci.
  • AliGatorAliGator Membre, Modérateur
    'Kixxx' a écrit:


    Je ne peux pas utiliser AFNetworking
    image/crazy.gif' class='bbc_emoticon' alt=' B) ' /> ah ? Pourquoi donc ?

    Mon pauvre, vu comment ça simplifie les choses, moi je ne peux plus m'en passer image/kiss.gif' class='bbc_emoticon' alt=':-*' />
  • Parce que je dois attaquer des services SOAP et pour me faciliter le développement j'ai utilisé l'outil wsdl2obj qui génère les classes d'accès, les objets et tout ce que j'ai besoin pour consommer les méthodes de mon webservice.
Connectez-vous ou Inscrivez-vous pour répondre.