James Eichele
James Eichele

Reputation: 119124

Is it possible to use Objective-C runtime features to determine where a method was called from?

Objective-C uses a sophisticated message-passing system when one object calls a method on another object. I want to know if it is possible, within the called method, to determine what the calling object was?

For example:

@implementation callingClass
- (void)performTest
{
    calledObject = [[[calledClass alloc] init] autorelease];
    id result = [calledObject calledMethod];

    assert(result == this);
}
@end

@implementation calledClass
- (id)calledMethod
{
    id objectThatCalledThisMethod = ... // <-- what goes here?

    return objectThatCalledThisMethod;
}
@end

What could I write in the commented line in order to make the assertion pass when I execute performTest?

Upvotes: 5

Views: 925

Answers (4)

intropedro
intropedro

Reputation: 2804

I 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:[origen  componentsSeparatedByCharactersInSet:separatorSet]];
    [array removeObject:@""];

    NSLog(@"Pila = %@", [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]);
    NSLog(@"Line caller = %@", [array objectAtIndex:5]);

Upvotes: 4

Harald Scheirich
Harald Scheirich

Reputation: 9764

You could try and derive your own class from NSInvocation that carries the caller information. Or wrap a class around NSInvocation reimplementing some of the calls in there.

Upvotes: 2

Peter N Lewis
Peter N Lewis

Reputation: 17811

No, you cannot determine what object called you. Well, technically, it might be possible to poke around the stack back trace, but certainly its not practical for real code.

If you look at most of the delegate methods, you can see that the standard delegate call formats look like this:

- (NSSize) windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize;
- (BOOL) windowShouldClose:(id)window;
- (void) windowWillMove:(NSNotification *)notification;

Note how the window (caller) is passed as the first argument, and how "window" is the first part of the method name. In the last case, the window caller is implicit in the NSNotification (notification.object is the window).

Upvotes: 2

Chuck
Chuck

Reputation: 237010

Not with the runtime. All message sends ultimately work out to a function call along the lines of objc_msgSend(id receiver, SEL selector, /*method arguments*/...). As you can see, no information is passed about the object sending the message. It's probably possible to determine the calling object by walking the stack, but that way lies madness. The only practical way to tell who called the method is to give it a sender argument like all IBAction methods have.

Upvotes: 11

Related Questions