35 votes

A la Cible-Action modèle de conception est devenue une mauvaise pratique en vertu de l'ARC?

Pendant des années, j'ai été à la suite d'un grand modèle appelé Cible-Action qui va comme ceci:

Un objet appelle un sélecteur de spécifié sur une cible précise de l'objet lorsque vient le temps de les appeler. Ceci est très utile dans beaucoup de cas différents où vous avez besoin d'un simple rappel à une méthode arbitraire.

Voici un exemple:

- (void)itemLoaded {
    [specifiedReceiver performSelector:specifiedSelector];
}

En vertu de l'ARC, il s'avère maintenant que faire quelque chose comme cela tout d'un coup, est devenu dangereux.

Xcode lance un avertissement qui va comme ceci:

PerformSelector peut provoquer une fuite parce que son sélecteur est inconnu

Bien sûr, le sélecteur est inconnue, puisque dans le cadre de la Cible-Action modèle de conception, vous pouvez spécifier quel que soit le sélecteur vous voulez afin d'obtenir un appel lorsque quelque chose d'intéressant se passe.

Ce qui me dérange le plus à propos de cet avertissement, c'est qu'il dit qu'il y a peut être un risque de fuite de mémoire. De ma compréhension de l'ARC ne pas plier la gestion de la mémoire règles, mais plutôt simplement automatise l'insertion de conserver/presse/autorelease messages au bon endroit.

Une autre chose à noter ici: -performSelector: avoir un id de la valeur de retour. ARC analyse les signatures de méthode pour comprendre, par l'application de conventions de nommage si la méthode renvoie un +1 conserver le comte objet ou pas. Dans ce cas, l'ARC ne sais pas si le sélecteur est un -newFooBar usine ou tout simplement à l'appel d'une unsuspicious travailleur méthode (qui est presque toujours le cas avec Cible-Action de toute façon). En fait l'ARC doit avoir reconnu que je n'ai pas s'attendre à un retour de la valeur, et donc oublier tout le potentiel de +1 conserver compté valeur de retour. En regardant cela de ce point de vue, je peux voir où l'ARC est à venir à partir de, mais il ya encore trop d'incertitude sur ce que cela signifie réellement dans la pratique.

N'est que maintenant dire sous l'ARC de quelque chose peut aller mal, qui ne serait jamais arrivé sans l'ARC? Je ne vois pas comment cela pourrait entraîner une fuite de mémoire. Quelqu'un peut-il donner des exemples de situations dans lesquelles c'est dangereux de le faire, et comment exactement une fuite est créé dans ce cas?

J'ai vraiment googlé l'enfer hors de l'internet mais n'ai pas trouvé de site en expliquant pourquoi.

25voto

mattjgalloway Points 24217

Le problème avec performSelector , c'est que l'ARC ne sais pas quel est le sélecteur qui sera effectué, n'. Considérez les points suivants:

id anotherObject1 = [someObject performSelector:@selector(copy)];
id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];

Maintenant, comment peut-ARC savons que le premier renvoie un objet à conserver un nombre de 1, mais le second renvoie un objet qui est autoreleased? (Je suis juste la définition d'une méthode appelée giveMeAnotherNonRetainedObject ici qui retourne quelque chose autoreleased). Si ça n'avait pas d'ajouter dans tous les rejets ensuite, anotherObject1 serait fuite ici.

Évidemment, dans mon exemple, les sélecteurs sont effectivement connus, mais imaginer qu'ils ont été choisis au moment de l'exécution. L'ARC ne pouvait vraiment pas faire son travail de mettre dans le bon nombre de retains ou releases ici, car il n'a tout simplement pas savoir ce que le sélecteur va faire. Vous avez raison, l'ARC n'est pas en tordre les règles et il est juste d'ajouter dans la bonne gestion de la mémoire des appels pour vous, mais c'est précisément la chose qu'il ne peut pas le faire ici.

Vous avez raison, le fait que vous êtes ignorant la valeur de retour signifie qu'il va être OK, mais en général, l'ARC est juste d'être pointilleux et d'avertissement. Mais je suppose que c'est pourquoi c'est un avertissement et non une erreur.

Edit:

Si vous êtes vraiment sûr que votre code est ok, vous avez juste à masquer l'avertissement comme suit:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop

9voto

Proud Member Points 7863

L'avertissement doit se lire comme ceci:

PerformSelector peut provoquer une fuite parce que son sélecteur est inconnue. L'ARC ne sais pas si le retour de l'id a un +1 conserver compter ou pas, et donc ne peut pas gérer correctement la mémoire de l'objet retourné.

Malheureusement, c'est juste la première phrase.

Maintenant la solution:

Si vous recevez une valeur de retour à partir d'un -performSelector méthode, vous ne pouvez pas faire quelque chose au sujet de l'avertissement dans le code, à l'exception de l'ignorer.

NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];

Votre meilleur pari est ceci:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
#pragma clang diagnostic pop

En va de même pour le cas de ma question initiale, où j'ai complètement ignorer la valeur de retour. L'ARC doit être assez intelligent pour voir que je n'aime pas à propos de le retour de l'id, et donc l'anonyme sélecteur est presque assuré de ne pas être une usine, la commodité, le constructeur ou que ce soit. Malheureusement, l'ARC n'est pas, de sorte que la même règle s'applique. Ignorer l'avertissement.

Il peut également être fait pour l'ensemble du projet par la mise en au -Wno-arc-performSelector-fuites compilateur drapeau sous la rubrique "Autres signaux d'alarme" dans le projet de paramètres de construction.

Alternativement, vous pouvez réduire l'avertissement sur un fichier par fichier lorsque vous ajoutez que du pavillon en vertu de Votre Cible > "Phases de construction" > "Compiler les Sources" sur le côté droit à côté du fichier désiré.

Tous les trois solutions sont très salissant à mon humble avis, j'espère donc que quelqu'un arrive avec un meilleur.

4voto

Plamen Terziev Points 96

Comme décrit ci-dessus, vous obtenez cet avertissement, car le compilateur ne sait pas où (ou si) de mettre le conserver/libération de la performSelector: valeur de retour.

Mais notez que si vous utilisez [someObject performSelector:@selector(selectorName)] il ne générera pas de mises en garde (au moins dans Xcode 4.5 avec llvm 4.1) parce que l'exacte sélecteur est facile à déterminer (à vous de la définir explicitement) et c'est pourquoi le compilateur est capable de mettre la conserver/communiqués à la bonne place.

C'est pourquoi, vous obtiendrez un avertissement uniquement si vous passez le sélecteur à l'aide de SEL pointeur, car dans ce cas le compilateur n'est pas en mesure de déterminer dans tous les cas quoi faire. Donc, en utilisant la suite

SEL s = nil;
if(condition1) SEL = @selector(sel1)
else SEL = @selector(sel2)

[self performSelector:s];

va générer d'alerte. Mais la refactorisation être:

if(condition1) [self performSelector:@selector(sel1)]
else [self performSelector:@selector(sel2)]

ne génèrent pas de toutes les mises en garde

3voto

Aaron Hayman Points 5421

L'ARC est en train de lancer l'avertissement parce qu'il ne peut pas garantir que le sélecteur n'est pas la création d'un objet qu'il ne connaît pas. Vous pourriez théoriquement recevoir quelque chose à partir de cette méthode que l'ARC ne peut pas gérer:

id objectA = [someObject performSelector:@selector(createObjectA)];

Peut-être un jour, mais pour l'instant il ne peut pas. (Remarque si il connaît l'objet (ce n'est pas une pièce d'identité) il ne jette pas cet avertissement).

Si vous essayez d'exécuter une méthode sans recevoir un objet de recul, je recommande d'utiliser objc_msgSend. Mais vous avez obtenu d'inclure dans votre classe:

#include <objc/message.h>
objc_msgSend(someObject, action);

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