2010-12-12 44 views
11

Appelle une méthode sur super prise en charge dans la mise en oeuvre d'un bloc Objective-C?Appeler super dans l'implémentation d'un bloc Objective-C

Quand j'appelle une méthode sur super un EXC_BAD_ACCESS erreur serait, mais dès que je l'ai changé ces appels de [super methodToCall] à [self methodToCall] et laisser le message déplacer la chaîne de répondeur il a bien fonctionné.

Il n'y a aucune implémentation de -methodToCall dans l'instance de la classe dans laquelle le bloc existe, mais il y en a une dans la superclasse (c'est-à-dire la classe dont elle hérite). Je suis juste curieux d'apprendre les détails sur pourquoi appeler une méthode sur super dans l'implémentation d'un bloc a été un problème en premier lieu (techniquement) afin que je puisse l'éviter à l'avenir. Je soupçonne que c'est lié à la façon dont les variables sont capturées dans le bloc et quelque chose à propos de la pile et du tas, mais je n'ai vraiment aucune idée concrète.

Remarque: le code d'implémentation de bloc est appelé jusqu'à quelques secondes après le stockage du bloc dans une propriété. La propriété utilise copy. Je ne pense donc pas que le cycle de vie du bloc pose problème. être bien. De plus, cela ne faisait que planter sur l'iPhone (3G) mais fonctionnait sans crash dans le simulateur de l'iPhone.

Résultats dans EXC_BAD_ACCESS:

[self retrieveItemsForId:idString completionHandler:^(NSError *error) { 
if (!error) { 
    [super didRetrieveItems]; 
} else { 
    [super errorRetrievingItems]; 
} 
}]; 

fonctionne parfaitement, les implémentations de -didRetrieveItems et -errorRetrievingItems sont dans la super-classe.

[self retrieveItemsForId:idString completionHandler:^(NSError *error) { 

if (!error) { 
    [self didRetrieveItems]; 
} else { 
    [self errorRetrievingItems]; 
} 
}]; 

Répondre

11

Techniquement, cela est un problème avec le moteur d'exécution Objective-C, et les mécanismes sous-jacents de la façon dont les appels à super fonctionnent réellement. Fondamentalement, ils capturent à la fois l'objet qui est le destinataire du message (self dans tous les cas) et la classe qui implémente la version spécifique de la méthode (la superclasse de la classe dans laquelle l'implémentation de la méthode a lieu). Étant donné qu'une grande partie de la préparation d'un tel message se produit au moment de la compilation, et non lors de l'exécution, je ne serais pas surpris s'il interagissait mal avec les blocs.

Je vérifie pour voir si self est toujours valide lorsque le message est sur le point d'être envoyé. Normalement, tous les objets référencés dans un bloc sont automatiquement conservés. Puisque super fonctionne un peu différemment, cela peut signifier que self ne sera pas retenu comme on pourrait s'y attendre. Un moyen facile de vérifier cela serait d'utiliser les appels à super comme écrit à l'origine, et de faire simplement couler l'objet appelé self, et voir si cela fonctionne. Si cela s'avère être le problème, vous devrez peut-être insérer une référence factice à self dans le bloc pour obtenir cette gestion automatique de la mémoire.

Dans le sens le plus strict, cependant, je ne suis pas sûr que vous pouvez compter sur ce travail pour toujours et toujours. Bien que les blocs puissent capturer l'état d'exécution actuel, cela n'a pas vraiment de sens (d'un point de vue OOP) de rompre l'encapsulation et d'invoquer des implémentations de superclasse, puisque le niveau niveau auquel les méthodes sont implémentées doit être opaque à tout appel externe code. J'essaierais de trouver une autre solution qui ne dépende pas de la hiérarchie d'héritage.

+0

Merci pour votre explication détaillée, j'apprécie! – Andrew

+0

Pas de problème. C'est certainement une question intéressante qui ne m'était pas venue à l'esprit auparavant. –

8

Résultats dans EXC_BAD_ACCESS:

[self retrieveItemsForId:idString completionHandler:^(NSError *error) { 
if (!error) { 
    [super didRetrieveItems]; 
} else { 
    [super errorRetrievingItems]; 
} 
}]; 

probablement en raison d'un bogue dans le compilateur; essayez d'ajouter [self class]; ou toute autre méthode appel à soi dans ce bloc et cela fonctionnera probablement.

Fonctionne parfaitement, les implémentations de -didRetrieveItems et -errorRetrievingItems sont dans la super-classe.

[self retrieveItemsForId:idString completionHandler:^(NSError *error) { 

if (!error) { 
    [self didRetrieveItems]; 
} else { 
    [self errorRetrievingItems]; 
} 
}]; 

Je pense que vous pouvez être confus au sujet de l'un des aspects fondamentaux de la programmation orientée objet. Vous dites qu'il n'y a pas d'implémentation de ces méthodes dans votre classe, elles n'existent que dans la superclasse.

En raison de l'héritage, votre classe répond également aux appels de méthode. Il suffit de les appeler comme vous le faites ci-dessus en utilisant self. Il va travailler trouver et est exactement comment vous devriez le faire!

+0

Doh, parfois je tape juste sans vraiment penser à ce que je fais! Pour une raison que j'ignorais, je pensais juste qu'elle remontait la chaîne des répondeurs, je ne pensais pas assez loin pour réaliser qu'elle héritait simplement des méthodes des super classes. Un tel principe de base à négliger, vous êtes sur! Merci d'avoir porté cela à mon attention. – Andrew