3

我有一个门面单例,我想将一些类方法调用转发给一些“静态”类。

乍一看,forwardInvocation:似乎是一个可能的解决方案,但是,NSInvocation'sinvokeWithTarget:并且setTarget:只接受一个id,即指向实例的指针,而不是类本身。我尝试将其作为目标移交[MyTargetClass class],但是当我在[Facade someForwardedMethod]某处调用时,仍然会收到“No known class method [...]”错误。当我打电话时,[[Facade sharedInstance] someForwardedMethod]我收到“没有可见的@interface [...] 声明选择器 [...]”错误。

当然我知道我还需要重写respondsToSelector:and 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];
    }
}

有没有办法使这项工作,还是我必须选择另一种方法?


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

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

[(id)[Facade sharedInstance] doSomethingClassyInTargetClass];

它比我希望的要丑一些,但它确实有效。但是,当我处理外观类时,我无法在目标类中调用类方法。为了让编译器安静,我可以写

[(Class)[Facade class] doSomethingClassyInTargetClass];

但随后它会在运行时抛出一个 'NSInvalidArgumentException' “[Facade doSomethingClassyInTargetClass]: unrecognized selector sent to class [...]”。显然,门面类的类方法在不尊重的情况下得到解决forwardInvocation:,但毕竟它-前面有一个......

4

1 回答 1

5

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

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

[obj doSomething];

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

[(id)obj doSomething];

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

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

[(Class)MyClass doSomethingClassy];

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

你还应该看看forwardTargetForSelector:Ken Thomases 的参考资料。


您不必强制sharedInstance转换为id. 只需更改签名,因为您总是希望以这种方式使用它:

+ (id)sharedInstance;
于 2012-06-02T18:50:13.637 回答