Existe uma maneira de determinar a linha de código a partir da qual um determinado method
foi chamado?
Existe uma maneira de determinar a linha de código a partir da qual um determinado method
foi chamado?
Respostas:
StackEu espero que isso ajude:
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]);
Em um código totalmente otimizado, não existe uma maneira 100% infalível de determinar o chamador de um determinado método. O compilador pode empregar uma otimização de chamada final, enquanto o compilador reutiliza efetivamente o frame de pilha do chamador para o receptor.
Para ver um exemplo disso, defina um ponto de interrupção em qualquer método usando gdb e observe o backtrace. Observe que você não vê objc_msgSend () antes de cada chamada de método. Isso ocorre porque objc_msgSend () faz uma chamada final para a implementação de cada método.
Embora seja possível compilar seu aplicativo não otimizado, você precisaria de versões não otimizadas de todas as bibliotecas do sistema para evitar apenas este problema.
E este é apenas um problema; na verdade, você está perguntando "como faço para reinventar o CrashTracer ou o gdb?". Um problema muito difícil sobre o qual as carreiras são construídas. A menos que você queira que suas "ferramentas de depuração" sejam sua carreira, eu não recomendaria seguir esse caminho.
Que pergunta você realmente está tentando responder?
Usando a resposta fornecida por intropedro , eu vim com o seguinte:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
que simplesmente me retornará a classe e função originais:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps - se a função for chamada usando performSelector, o resultado será:
Origin: [NSObject performSelector:withObject:]
Acabei de escrever um método que fará isso por você:
- (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;
}
A versão Swift 2.0 da resposta de @Intropedro para referência;
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])")
Se for para depurar, adquira o hábito de colocar um NSLog(@"%s", __FUNCTION__);
Como a primeira linha dentro de cada método em suas classes. Assim, você sempre pode saber a ordem das chamadas de método olhando para o depurador.
Você pode passar self
como um dos argumentos para a função e, em seguida, obter o nome da classe do objeto chamador dentro de:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
Dessa forma, você pode passar para ele qualquer objeto que o ajude a determinar onde pode estar o problema.
Uma versão ligeiramente otimizada da resposta fantástica 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];
Na janela de saída, você verá algo como o seguinte.
Chamador: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
Você também pode analisar essa string para extrair mais dados sobre o quadro de pilha.
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
Foi retirado de Identificar método de chamada no iOS .
A versão Swift 4 de @Geoff H responde por copiar e colar ;]
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])")
A versão Swift 3 de @Geoff H responde para referência:
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])")