是否可以使用Objective-C运行时功能来确定调用方法的位置?

时间:2022-06-13 11:00:11

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?

当一个对象在另一个对象上调用方法时,Objective-C使用复杂的消息传递系统。我想知道在被调用的方法中是否有可能确定调用对象是什么?

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?

我可以在注释行中写什么,以便在执行performTest时使断言通过?

4 个解决方案

#1


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.

不是运行时。所有消息发送最终都会在objc_msgSend(id接收器,SEL选择器,/ *方法参数* / ...)的行中进行函数调用。如您所见,没有传递有关发送消息的对象的信息。通过遍历堆栈来确定调用对象可能是可能的,但这种方式就是疯狂。告诉谁调用该方法的唯一实用方法是给它一个像所有IBAction方法一样的发送者参数。

#2


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

#3


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).

注意窗口(调用者)如何作为第一个参数传递,以及“window”如何作为方法名称的第一部分。在最后一种情况下,窗口调用者隐含在NSNotification中(notification.object是窗口)。

#4


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.

您可以尝试从携带调用者信息的NSInvocation派生自己的类。或者围绕NSInvocation包装一个类,重新实现那里的一些调用。

#1


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.

不是运行时。所有消息发送最终都会在objc_msgSend(id接收器,SEL选择器,/ *方法参数* / ...)的行中进行函数调用。如您所见,没有传递有关发送消息的对象的信息。通过遍历堆栈来确定调用对象可能是可能的,但这种方式就是疯狂。告诉谁调用该方法的唯一实用方法是给它一个像所有IBAction方法一样的发送者参数。

#2


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

#3


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).

注意窗口(调用者)如何作为第一个参数传递,以及“window”如何作为方法名称的第一部分。在最后一种情况下,窗口调用者隐含在NSNotification中(notification.object是窗口)。

#4


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.

您可以尝试从携带调用者信息的NSInvocation派生自己的类。或者围绕NSInvocation包装一个类,重新实现那里的一些调用。