Obj-C内省:一个方法如何引用它所调用的参数

时间:2013-05-02 17:39:23

标签: ios objective-c selector introspection objective-c-runtime

这是问题的故意“分叉”: Obj-C introspection: How can a method reference its own selector?

我需要做与OP完全相同的事情,但是他不需要传递任何参数,我这样做。



我有办法调用一个带有多个未知参数的方法(NSInvocation)。我有一些方便的方法可以帮助我,比如question。但是,必须在需要这样做的每个类中复制每个方法的工作量会导致维护问题,并且当错误的参数被传递(或者以错误的顺序)到方便方法时会导致地狱般的调试。

这本身就是一个相对较小的问题,但我已经努力了两年,并想解决。通过使用单行宏(如@ Michael57)来调用我的方便方法,自动检查选择器(使用_cmd)和参数(使用?)并按照我的意愿调用方便方法。


请注意,这适用于具有固定参数列表且不是可变参数的方法。所以我不确定va_args方法是否适用于检查参数。

所以从代码管理的角度来看,纯粹出于技术的好奇心(如果选择器存在_cmd,那么必须有一些参数?)我想知道如何可以反省标准的Obj-C方法参数。


我希望这是非常明显的我不知道的事情。谢谢您的帮助!


编辑:一些示例代码: 我正在尝试做的基本目的是始终在单个线程中运行在某个实例上调用的方法。为此,我在NSObject类别中有一个类似的方便方法:

- (void)performSelectorInBackground:(SEL)aSelector waitUntilDone:(BOOL)waitUntilDone withArgumentLocations:(void *)firstArg,...;

它在特定后台线程中的目标对象上运行被调用的选择器,该线程专门映射到该实例。所有这些只是为了避免一些可能发生的粘性同步问题。

首先,也许所有这些都是kludgy,但我已经过去了。此代码集成在太多地方。 :)

“withArgumentLocations”可变参数列表是一个指针位置列表,它可以同时包含标量值和常规Obj-C对象,因此通常可以在后台调用带有可变数量和参数类型的选择器简单的1行事件。


无论如何,实际用例如下:


- (void)loginRequestWithUsername:(NSString *)username andPassword:(NSString *)password {

if(![self isInBackgroundThread])
{
    [self performSelectorInBackground:@selector(loginRequestWithUsername:andPassword:) waitUntilDone:NO withArgumentLocations:&username, &password, nil];
    return;
}

if(![self isInBackgroundThread]) { [self performSelectorInBackground:@selector(loginRequestWithUsername:andPassword:) waitUntilDone:NO withArgumentLocations:&username, &password, nil]; return; }

是另一种方便方法,用于检查当前执行线程是否为此实例的“已分配”线程。

因此,这种方法适用于单个方法调用,但现在我想将其扩展到类中的所有方法。当“argumentLocations”没有被正确替换,或者没有以正确的顺序替换,导致地狱般的调试时,这有时会导致问题。


这就是为什么我想用一个小宏替换上面的代码块,我可以简单地粘贴到每个方法中以实现我需要的东西。


这就是为什么这个宏应该能够查询自己的选择器(_cmd)和参数(使用?)并将它们传递给我的[self isInBackgroundThread]方法。

1 个答案:

答案 0 :(得分:0)

关于获取NSInvocation,首先想到的是您无法实现该方法,然后使用正确构造的-forwardInvocation:调用NSInvocation。但我想这不是直接有用的,因为你需要实现方法并在方法中做一些事情。但是,也许你在-forwardInvocation:中可以做的是调用另一个对象的调用(“转发调用”),其中该对象上的方法执行真实的东西。然后,您还可以将调用存储在该方法可以访问的某个位置(如有必要)。或者也许你可以在-forwardInvocation:本身中执行你想要的“重新运行”逻辑。