ios - Objetivo C encontrar al que llama del método
objective-c debugging (11)
¿Hay alguna forma de determinar la línea de código desde la que se llamó a un determinado method
?
@ennuikiller
//Add this private instance method to the class you want to trace from
-(void)trace
{
//Go back 2 frames to account for calling this helper method
//If not using a helper method use 1
NSArray* stack = [NSThread callStackSymbols];
if (stack.count > 2)
NSLog(@"Caller: %@", [stack objectAtIndex:2]);
}
//Add this line to the method you want to trace from
[self trace];
En la ventana de salida, verá algo como lo siguiente.
Llamante: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
También puede analizar esta cadena para extraer más datos sobre el marco de la pila.
2 = Thread id
My App = Your app name
0x0004e8ae = Memory address of caller
-[IINClassroomInit buildMenu] = Class and method name of caller
+86 = Number of bytes from the entry point of the caller that your method was called
Fue tomado de Identify Calling Method en iOS .
En un código completamente optimizado, no hay una forma 100% segura de determinar quién llama a un determinado método. El compilador puede emplear una optimización de cola de llamada mientras que el compilador reutiliza eficazmente el marco de pila de la persona que llama para el destinatario.
Para ver un ejemplo de esto, establezca un punto de interrupción en cualquier método dado utilizando gdb y observe la traza inversa. Tenga en cuenta que no ve objc_msgSend () antes de cada llamada a un método. Eso es porque objc_msgSend () hace una llamada de cola a la implementación de cada método.
Si bien podría compilar su aplicación no optimizada, necesitaría versiones no optimizadas de todas las bibliotecas del sistema para evitar este único problema.
Y este es solo un problema; en efecto, usted está preguntando "¿cómo reinicio CrashTracer o gdb?". Un problema muy difícil sobre el cual se hacen carreras. A menos que desee que su "carrera profesional" sea una herramienta de depuración, recomendaría no seguir por este camino.
¿Qué pregunta estás tratando de responder?
La versión Swift 2.0 de la respuesta de @Intropedro como referencia;
let sourceString: String = NSThread.callStackSymbols()[1]
let separatorSet :NSCharacterSet = NSCharacterSet(charactersInString: " -[]+?.,")
let array = NSMutableArray(array: sourceString.componentsSeparatedByCharactersInSet(separatorSet))
array.removeObject("")
print("Stack: /(array[0])")
print("Framework:/(array[1])")
print("Memory Address:/(array[2])")
print("Class Caller:/(array[3])")
print("Method Caller:/(array[4])")
La versión Swift 4 de @Geoff H responde para copiar y pegar ;]
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet :CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
var array = Array(sourceString.components(separatedBy: separatorSet))
array = array.filter { $0 != "" }
print("Stack: /(array[0])")
print("Framework:/(array[1])")
print("Memory Address:/(array[2])")
print("Class Caller:/(array[3])")
print("Method Caller:/(array[4])")
La versión de Swift 3 de @Geoff H responde para referencia:
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet: CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
let array = NSMutableArray(array: sourceString.components(separatedBy: separatorSet))
array.remove("")
print("Stack: /(array[0])")
print("Framework:/(array[1])")
print("Memory Address:/(array[2])")
print("Class Caller:/(array[3])")
print("Method Caller:/(array[4])")
Puede pasar self
como uno de los argumentos a la función y luego obtener el nombre de clase del objeto que llama dentro de:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
De esta forma, puede pasarle cualquier objeto que lo ayude a determinar dónde podría estar el problema.
Si se trata de eliminar el sake, adquiera el hábito de poner un NSLog(@"%s", __FUNCTION__);
Como la primera línea dentro de cada método en tus clases. Entonces siempre se puede saber el orden de las llamadas a los métodos al mirar el depurador.
Simplemente escribió un método que hará esto por usted:
- (NSString *)getCallerStackSymbol {
NSString *callerStackSymbol = @"Could not track caller stack symbol";
NSArray *stackSymbols = [NSThread callStackSymbols];
if(stackSymbols.count >= 2) {
callerStackSymbol = [stackSymbols objectAtIndex:2];
if(callerStackSymbol) {
NSMutableArray *callerStackSymbolDetailsArr = [[NSMutableArray alloc] initWithArray:[callerStackSymbol componentsSeparatedByString:@" "]];
NSUInteger callerStackSymbolIndex = callerStackSymbolDetailsArr.count - 3;
if (callerStackSymbolDetailsArr.count > callerStackSymbolIndex && [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex]) {
callerStackSymbol = [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex];
callerStackSymbol = [callerStackSymbol stringByReplacingOccurrencesOfString:@"]" withString:@""];
}
}
}
return callerStackSymbol;
}
StackEspero que esto ayude:
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]);
Una versión ligeramente optimizada de la fantástica respuesta de @Roy Kronenfeld:
- (NSString *)findCallerMethod
{
NSString *callerStackSymbol = nil;
NSArray<NSString *> *callStackSymbols = [NSThread callStackSymbols];
if (callStackSymbols.count >= 2)
{
callerStackSymbol = [callStackSymbols objectAtIndex:2];
if (callerStackSymbol)
{
// Stack: 2 TerribleApp 0x000000010e450b1e -[TALocalDataManager startUp] + 46
NSInteger idxDash = [callerStackSymbol rangeOfString:@"-" options:kNilOptions].location;
NSInteger idxPlus = [callerStackSymbol rangeOfString:@"+" options:NSBackwardsSearch].location;
if (idxDash != NSNotFound && idxPlus != NSNotFound)
{
NSRange range = NSMakeRange(idxDash, (idxPlus - idxDash - 1)); // -1 to remove the trailing space.
callerStackSymbol = [callerStackSymbol substringWithRange:range];
return callerStackSymbol;
}
}
}
return (callerStackSymbol) ?: @"Caller not found! :(";
}
Utilizando la respuesta provista por intropedro , se me ocurrió esto:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
que simplemente me devolverá la clase y función originales:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps: si se llama a la función utilizando performSelector, el resultado será:
Origin: [NSObject performSelector:withObject:]