Objective Cfind方法的调用者
有没有一种方法来确定某个method
被调用的代码行?
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]);
在完全优化的代码中,没有100%确定的方法来确定调用者使用某种方法。 编译器可以使用尾部调用优化,而编译器有效地重新使用调用者的被调用者的栈帧。
要看到这个例子,使用gdb在任何给定的方法上设置一个断点,并看看回溯。 请注意,在每次方法调用之前,都没有看到objc_msgSend()。 这是因为objc_msgSend()会对每个方法的实现进行尾部调用。
虽然您可以编译未优化的应用程序,但您需要所有系统库的非优化版本才能避免这个问题。
这只是一个问题, 实际上,你是在问“我该如何重新发明CrashTracer或gdb?”。 一个非常困难的问题,在哪个职业上。 除非你想“debugging工具”成为你的职业,否则我会build议不要走这条路。
你真的想回答什么问题?
使用intropedro提供的答案 ,我想出了这个:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
这将简单地返回我原来的类和function:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps – 如果使用performSelector调用函数,结果将是:
Origin: [NSObject performSelector:withObject:]
@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])")
如果是为了debbuging的缘故,养成放置NSLog(@"%s", __FUNCTION__);
的习惯NSLog(@"%s", __FUNCTION__);
作为类中每个方法的第一行。 然后,您可以始终通过查看debugging器来了解方法调用的顺序。
您可以将self
作为参数之一传递给函数,然后获取调用者对象的类名称:
+(void)log:(NSString*)data from:(id)sender{ NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data); } //... -(void)myFunc{ [LoggerClassName log:@"myFunc called" from:self]; }
这样你就可以传递任何对象来帮助你确定问题的出现点。
刚刚写了一个方法,将为你做这个:
- (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; }
@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];
在输出窗口中,您将看到如下所示的内容。
来电者:2 MyApp 0x0004e8ae – [IINClassroomInit buildMenu] + 86
你也可以parsing这个string来提取更多关于栈帧的数据。
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
它来自iOS中的识别调用方法 。
@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])")