91 votes

Objective-C trouve l'appelant de la méthode

Y a-t-il un moyen de déterminer la ligne de code d'un certain method a été appelé ?

0 votes

Pourquoi voulez-vous faire ça ? Si c'est pour le débogage, il y a un ensemble de réponses très différentes que si vous voulez le faire en production (pour lequel la réponse est plus probablement "ne le faites pas").

4 votes

Je vais prendre la réponse du débogage

3 votes

Y a-t-il une réponse de la production ?

190voto

intropedro Points 1206

StackJ'espère que cela vous aidera :

NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1];
// Example: 1   UIKit                               0x00540c89 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163
NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"];
NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString  componentsSeparatedByCharactersInSet:separatorSet]];
[array removeObject:@""];

NSLog(@"Stack = %@", [array objectAtIndex:0]);
NSLog(@"Framework = %@", [array objectAtIndex:1]);
NSLog(@"Memory address = %@", [array objectAtIndex:2]);
NSLog(@"Class caller = %@", [array objectAtIndex:3]);
NSLog(@"Function caller = %@", [array objectAtIndex:4]);

1 votes

J'ai également créé une macro dans le fichier -Prefix.pch et je l'ai exécutée à partir du délégué de l'application. De façon intéressante, l'appelant de la classe était : "<redacted>"

5 votes

Dans mon cas, il n'y a rien à l'indice 5. Ce code a donc fait planter mon application. Il a fonctionné après avoir supprimé la dernière ligne. Néanmoins, c'est toujours si génial que ça vaut le coup d'ajouter un +1 !

1 votes

Cela fonctionne très bien, mais comment interpréter le "line caller" ? Dans mon cas, il affiche un numéro, par exemple 91, mais pourquoi 91 ? Si je déplace l'appel une instruction plus bas, il affichera 136... Alors comment ce nombre est-il calculé ?

54voto

bbum Points 124887

Dans un code entièrement optimisé, il n'existe pas de moyen sûr à 100 % pour déterminer l'appelant d'une certaine méthode. Le compilateur peut employer une optimisation de l'appel de queue alors que le compilateur réutilise effectivement le cadre de la pile de l'appelant pour l'appelé.

Pour voir un exemple de ceci, placez un point d'arrêt sur une méthode donnée en utilisant gdb et regardez le backtrace. Notez que vous ne voyez pas objc_msgSend() avant chaque appel de méthode. C'est parce que objc_msgSend() fait un appel de queue à l'implémentation de chaque méthode.

Bien que vous puissiez compiler votre application sans optimisation, vous auriez besoin de versions non optimisées de toutes les bibliothèques système pour éviter ce seul problème.

Et ce n'est qu'un seul problème ; en fait, vous demandez "comment réinventer CrashTracer ou gdb". Un problème très difficile sur lequel les carrières sont faites. À moins que vous ne souhaitiez faire des "outils de débogage" votre carrière, je vous déconseille de vous engager dans cette voie.

À quelle question essayez-vous vraiment de répondre ?

3 votes

OH MY GOD. Cela m'a ramené sur terre. Presque littéralement. Je résolvais un problème qui n'avait rien à voir. Merci SIR !

11voto

Guntis Treulands Points 3354

En utilisant la réponse fournie par intropedro j'ai trouvé ça :

#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])

qui me renverra simplement la classe et la fonction d'origine :

2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]

p.s. - si la fonction est appelée en utilisant performSelector, le résultat sera :

Origin: [NSObject performSelector:withObject:]

2 votes

* Mais attention, dans certains cas, il ne contient pas le nom de la fonction, ni le sélecteur d'exécution, et donc - l'appel de CALL_ORIGIN plante. (Donc, je conseille - si vous allez utiliser cet exemple, utilisez-le temporairement et ensuite supprimez-le).

5voto

niraj Points 124

Si c'est pour le débogage, prenez l'habitude de mettre un NSLog(@"%s", __FUNCTION__);

Comme première ligne à l'intérieur de chaque méthode dans vos classes. Vous pourrez ainsi toujours connaître l'ordre d'appel des méthodes en regardant le débogueur.

0 votes

D'une certaine manière, le code ne s'affiche pas correctement. Il y a deux traits de soulignement avant et après FUNCTION.

0 votes

Essayez d'utiliser les guillemets (`) pour entourer votre code afin qu'il s'affiche correctement.

3 votes

Ou mieux, utilisez __PRETTY_FUNCTION__ qui supporte également l'Objective-C et affiche le nom de l'objet ainsi que la méthode.

4voto

pckill Points 1463

Vous pouvez passer self comme l'un des arguments de la fonction et ensuite obtenir le nom de classe de l'objet appelant à l'intérieur :

+(void)log:(NSString*)data from:(id)sender{
    NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}

//...

-(void)myFunc{
    [LoggerClassName log:@"myFunc called" from:self];
}

De cette façon, vous pouvez lui transmettre tout objet qui vous aiderait à déterminer où se situe le problème.

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