¿Hay alguna forma de determinar la línea de código method
desde la que se llamó a un determinado ?
¿Hay alguna forma de determinar la línea de código method
desde la que se llamó a un determinado ?
Respuestas:
Stack Espero 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]);
En un código totalmente optimizado, no existe una forma 100% segura de determinar quién llama a un método determinado. El compilador puede emplear una optimización de llamada de cola mientras que el compilador reutiliza efectivamente el marco de pila del llamador para el destinatario.
Para ver un ejemplo de esto, establezca un punto de interrupción en cualquier método dado usando gdb y observe el backtrace. Tenga en cuenta que no ve objc_msgSend () antes de cada llamada a un método. Eso es porque objc_msgSend () hace una llamada final 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 solo este problema.
Y este es solo un problema; en efecto, se está preguntando "¿cómo reinvento CrashTracer o gdb?". Un problema muy duro sobre el que se hacen las carreras. A menos que desee que las "herramientas de depuración" sean su carrera, le recomendaría no seguir este camino.
¿Qué pregunta estás realmente tratando de responder?
Usando la respuesta proporcionada 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 mediante performSelector, el resultado será:
Origin: [NSObject performSelector:withObject:]
Acabo de escribir un método que hará esto por ti:
- (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;
}
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])")
Si es por depurar errores, adquiera el hábito de poner un NSLog(@"%s", __FUNCTION__);
Como la primera línea dentro de cada método en sus clases. Entonces siempre puede saber el orden de las llamadas al método mirando al depurador.
Puede pasar self
como uno de los argumentos a la función y luego obtener el nombre de clase del objeto llamador dentro:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
De esta manera, puede pasarle cualquier objeto que le ayude a determinar dónde podría estar el problema.
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! :(";
}
@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.
Persona que llama: 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
Se tomó de Identify Calling Method en iOS .
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 Swift 3 de @Geoff H responde como 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])")