ios Objective C 找到方法的调用者

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1451342/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:25:23  来源:igfitidea点击:

Objective C find caller of method

iosobjective-cdebuggingnsthread

提问by ennuikiller

Is there a way to determine the line of code a certain methodwas called from?

有没有办法确定某个method调用的代码行?

回答by intropedro

StackI hope that this helps:

堆栈我希望这有助于:

    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]);

回答by bbum

In fully optimized code, there is no 100% surefire way to determine the caller to a certain method. The compiler may employ a tail call optimization whereas the compiler effectively re-uses the caller's stack frame for the callee.

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

To see an example of this, set a breakpoint on any given method using gdb and look at the backtrace. Note that you don't see objc_msgSend() before every method call. That is because objc_msgSend() does a tail call to each method's implementation.

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

While you could compile your application non-optimized, you would need non-optimized versions of all of the system libraries to avoid just this one problem.

虽然您可以编译非优化的应用程序,但您需要所有系统库的非优化版本才能避免这一问题。

And this is just but one problem; in effect, you are asking "how do I re-invent CrashTracer or gdb?". A very hard problem upon which careers are made. Unless you want "debugging tools" to be your career, I would recommend against going down this road.

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

What question are you really trying to answer?

你真正想回答什么问题?

回答by Guntis Treulands

Using answer provided by intropedro, I came up with this:

使用intropedro提供的答案,我想出了这个:

#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])

which will simply return me Original class and function:

这将简单地返回我原来的类和函数:

2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]

p.s. - if function is called using performSelector, result will be:

ps - 如果使用 performSelector 调用函数,结果将是:

Origin: [NSObject performSelector:withObject:]

回答by Roy K

Just wrote a method that will do this for you:

刚刚写了一个方法可以为你做到这一点:

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

回答by Geoff H

The Swift 2.0 version of @Intropedro's answer for reference;

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

回答by Giovanni

If it is for debbuging sake, get to the habit of putting a NSLog(@"%s", __FUNCTION__);

如果是为了调试,养成把一个 NSLog(@"%s", __FUNCTION__);

As the first line inside each method in your classes. Then you can always know the order of method calls from looking at the debugger.

作为类中每个方法的第一行。然后你总是可以通过查看调试器知道方法调用的顺序。

回答by pckill

You can pass selfas one of the arguments to the function and then get the classname of the caller object inside:

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

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

//...

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

This way you can pass it any object that would help you to determine where the problem might be.

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

回答by Andrew

A slightly optimized version of @Roy Kronenfeld's fantastic answer:

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

回答by DannyBios

@ennuikiller

@ennukiller

//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];

In the output window you will see something like the following.

在输出窗口中,您将看到如下所示的内容。

Caller: 2 MyApp 0x0004e8ae -[IINClassroomInit buildMenu] + 86

调用者:2 MyApp 0x0004e8ae -[IINClassroomInit buildMenu] + 86

You can also parse this string to extract more data about the stack frame.

您还可以解析此字符串以提取有关堆栈帧的更多数据。

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

It was taken from Identify Calling Method in iOS.

它取自iOS 中的识别调用方法

回答by Andy Obusek

The Swift 4 version of @Geoff H answer for copy and pasting;]

Swift 4 版本的@Geoff H 回答复制和粘贴;]

let sourceString: String = Thread.callStackSymbols[1]
let separatorSet :CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
var array = Array(sourceString.components(separatedBy: separatorSet))
array = array.filter { ##代码## != "" }

print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")