Question

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

Was it helpful?

Solution

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

OTHER TIPS

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.

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.

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.

What question are you really trying to answer?

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

#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:

Origin: [NSObject performSelector:withObject:]

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

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

If it is for debbuging sake, get to the habit of putting a 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.

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

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

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

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

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

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

Caller: 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.

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

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

The Swift 3 version of @Geoff H answer for reference:

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])")
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top