目标C查找方法的调用者


Answers:


188

我希望这会有所帮助:

    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
还在-Prefix.pch文件中创建了一个宏,然后从应用程序委托运行它。有趣的是,班级呼叫者是:“ <redacted>”
Melvin Sovereign

4
就我而言,索引5上没有任何内容。因此,此代码使我的应用程序崩溃了。删除最后一行后,它起作用了。尽管如此,它仍然很棒,值得+1!
布莱恩(Brian)

1
这很好用,但是我们如何解释“线路呼叫者”?以我为例,它显示一个数字,例如91,但为什么是91?如果我将呼叫移至下面的一条指令,它将显示136 ...那么该数字如何计算?
Maxim Chetrusca 2014年

@Pétur如果对性能的影响可以忽略不计,则NSThread已经拥有该信息,您基本上只是在访问一个数组并创建一个新数组。
奥斯卡·戈麦斯

它在调试模式下工作,但是将其归档到IPA包后,调用堆栈无法按预期工作。我刚得到“ callStackSymbols = 1 SimpleApp 0x00000001002637a4 _Z8isxdigiti + 63136”,“ _ Z8isxdigiti”应为“ AAMAgentAppDelegate application:didFinishLaunchingWithOptions:”
Alanc Liu

50

在完全优化的代码中,没有100%确定方法来确定特定方法的调用方。编译器可以采用尾部调用优化,而编译器可以为被调用者有效地重新使用调用者的堆栈帧。

要查看此示例,请使用gdb在任何给定方法上设置一个断点,然后查看回溯。请注意,在每个方法调用之前,您不会看到objc_msgSend()。这是因为objc_msgSend()对每个方法的实现进行尾部调用。

尽管您可以对应用程序进行非优化的编译,但是您需要所有系统库的非优化版本来避免这一问题。

这只是一个问题。实际上,您在问“我如何重新发明CrashTracer或gdb?”。职业发展的一个非常困难的问题。除非您希望“调试工具”成为您的职业,否则我建议您不要走这条路。

您真正要回答什么问题?


3
哦,我的上帝。这使我回到尘世。几乎从字面上看。我正在解决一个完全无关的问题。谢谢你,先生!
nimeshdesai 2014年

11

使用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]

ps-如果使用performSelector调用函数,则结果为:

Origin: [NSObject performSelector:withObject:]

2
*但是请注意,在某些情况下,它不包含函数名称,也不执行选择器,因此-调用CALL_ORIGIN会崩溃。(因此,我建议-如果您要使用此示例,请暂时使用它,然后将其删除。)
Guntis Treulands

6

刚刚编写了一种可以为您完成此操作的方法:

- (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

@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])")

5

如果是为了调试,请养成养成习惯。 NSLog(@"%s", __FUNCTION__);

作为类中每个方法内的第一行。这样,您始终可以通过查看调试器来了解方法调用的顺序。


代码不正确地以某种方式出现。功能前后有两个下划线
Giovanni

尝试使用反引号转义

3
或更好地使用__PRETTY_FUNCTION__,它也支持Objective-C并显示对象名称和方法。
Ben Sinclair 2014年

4

您可以self作为参数之一传递给函数,然后在内部获取调用者对象的类名:

+(void)log:(NSString*)data from:(id)sender{
    NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}

//...

-(void)myFunc{
    [LoggerClassName log:@"myFunc called" from:self];
}

这样,您可以将可以帮助您确定问题所在的任何对象传递给它。


3

@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! :(";
}

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

它取自iOS中的Identify Calling Method


2

@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])")

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])")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.