有没有办法确定从某个method
调用的代码行?
答案 0 :(得分:179)
StackI希望这会有所帮助:
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 :(得分:49)
在完全优化的代码中,没有100%可靠的方法来确定某个方法的调用者。编译器可以使用尾调用优化,而编译器有效地重用调用者的堆栈帧用于被调用者。
要查看此示例,请使用gdb在任何给定方法上设置断点,然后查看回溯。请注意,在每次方法调用之前都没有看到objc_msgSend()。这是因为objc_msgSend()对每个方法的实现进行尾调用。
虽然您可以编译未优化的应用程序,但您需要所有系统库的非优化版本才能避免这一问题。
这只是一个问题;实际上,你问“我如何重新发明CrashTracer或gdb?”。一个非常难以解决的职业问题。除非你想要“调试工具”成为你的职业,否则我建议不要走这条路。
你真的想回答什么问题?
答案 2 :(得分:9)
使用 intropedro 提供的答案,我提出了这个问题:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
这将简单地返回原始类和函数:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
P.S。 - 如果使用performSelector调用函数,则结果为:
Origin: [NSObject performSelector:withObject:]
答案 3 :(得分:6)
@ Intropedro的Swift 2.0版本的答案供参考;
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])")
答案 4 :(得分:5)
如果是为了改债,那就养成放NSLog(@"%s", __FUNCTION__);
作为类中每个方法的第一行。然后,您始终可以通过查看调试器来了解方法调用的顺序。
答案 5 :(得分:5)
刚刚写了一个能为你做到这一点的方法:
- (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;
}
答案 6 :(得分:4)
您可以将self
作为函数的一个参数传递给函数,然后获取调用者对象的类名:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
通过这种方式,您可以传递任何有助于确定问题所在位置的对象。
答案 7 :(得分:2)
@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];
在输出窗口中,您将看到如下内容。
来电:2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
您还可以解析此字符串以提取有关堆栈帧的更多数据。
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
答案 8 :(得分:2)
@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! :(";
}
答案 9 :(得分:1)
@ Geoff H的Swift 4版本回答复制和粘贴;]
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])")
答案 10 :(得分:0)
@ Geoff H的Swift 3版本的答案供参考:
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])")
答案 11 :(得分:0)
回到Objective-C中没有点语法的时代,所以现在它看起来像。
#define __UGLY__CALLEE__(idx) fprintf(stderr,"\n%s <- %s",__PRETTY_FUNCTION__,(NSThread.callStackSymbols.count>idx?((NSString*)NSThread.callStackSymbols[idx]).UTF8String:"no callStackSymbol with this index"))
只打印需要的内容,无需额外重新创建 NSArray 或 Mutables。除了要输出的字符和要选择的索引外,您还可以使用不同的堆栈符号进行重复,并在没有时间戳的情况下打印。额外格式化输出不仅会降低性能,直到您获得需要了解的方法调用信息,还会使事情变得不灵活。最重要的是不要引入另一个对 self 的方法调用,只是为了请求最后一个被调用者。
__UGLY__CALLEE__(1);
结果...
-[Some inspectedMethod] <- 1 Appname 0x00000001000e6cd2 -[SomeCallee method] + 1234
因为它不漂亮 - 它被称为丑陋。