forwardInvocation到其他类而不是实例

Ste*_*fan 3 objective-c

我有一个外观单例,我想将一些类方法调用转发给一些"静态"类.

乍一看,forwardInvocation:似乎是一个可行的解决方案,然而,NSInvocationinvokeWithTarget:而且setTarget:只接受一个id,即一个指向实例,而不是一类本身.我尝试将其[MyTargetClass class]作为目标移交,但是当我在[Facade someForwardedMethod]某个地方打电话时,我仍然会收到"没有已知的类方法[...]"错误.当我打电话时,[[Facade sharedInstance] someForwardedMethod]我得到一个"No visible @interface [...]声明选择器[...]"错误.

当然,我知道我还需要重写respondsToSelector:methodSignatureForSelector:,所以我的代码如下所示:

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([super respondsToSelector:aSelector]) {
        return YES;
    } else {
        return [MyTargetClass respondsToSelector:aSelector];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
        signature = [MyTargetClass methodSignatureForSelector:selector];
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    SEL aSelector = [anInvocation selector];

    if ([MyTargetClass respondsToSelector: aSelector]) {
        [anInvocation invokeWithTarget:[MyTargetClass class]];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法让这项工作,或者我必须选择另一种方法?


编辑:我已经尝试过Rob Napier在他的回答中提到的两种方式,这是我的发现:

我可以通过facade实例调用我的目标类中的类方法

[(id)[Facade sharedInstance] doSomethingClassyInTargetClass];
Run Code Online (Sandbox Code Playgroud)

这比我希望的有点丑,但它确实有效.但是,当我处理facade类时,我无法在目标类中调用类方法.为了使编译器安静,我可以写

[(Class)[Facade class] doSomethingClassyInTargetClass];
Run Code Online (Sandbox Code Playgroud)

但是它会在运行时抛出'NSInvalidArgumentException'"[Facade doSomethingClassyInTargetClass]:无法识别的选择器发送到类[...]".显然,门面类的类方法在不尊重的情况下得到解决forwardInvocation:,但毕竟它有一个-前面的...

Rob*_*ier 5

啊,当你添加"No visible @interface [...]声明选择器[...]"时,一切都清楚了.

考虑一个只有实例方法的简单形式.你实现-forwardInvocation:,然后你有这样的代码:

[obj doSomething];
Run Code Online (Sandbox Code Playgroud)

现在,MyObject实际上并没有doSomething在其标题中声明,即使它会响应它.所以编译器抱怨这可能不起作用.解决方法是:

[(id)obj doSomething];
Run Code Online (Sandbox Code Playgroud)

声明某些内容时id,编译器会停止检查目标是否实际实现了选择器.但也有可能没有接口声明doSomething.然后编译器再次怀疑它可能是一个错字,并给你一个警告.并且通过"无接口",我的意思是"没有编译器可以访问的接口." 编译器只能访问此编译单元中的接口(即.m文件及其包含的头文件).因此,您需要确保包含定义此选择器的标头.

现在对于类,你不能使用id,但你应该能够Class用来实现相同的东西,例如:

[(Class)MyClass doSomethingClassy];
Run Code Online (Sandbox Code Playgroud)

如果可能想要查看来自iOS 的RNObserverManager示例代码:PTL第4章,它演示了类似的内容.

您还应该看看forwardTargetForSelector:Ken Thomases的参考资料.


你不必投sharedInstanceid.只需更改签名,因为您总是希望以这种方式使用它:

+ (id)sharedInstance;
Run Code Online (Sandbox Code Playgroud)