我有一个facade单例,我想将一些类方法调用转发给一些“静态”类。
乍一看,forwardInvocation:
似乎是一种可能的解决方案,但NSInvocation
的{{1}}和invokeWithTarget:
只接受setTarget:
,i。即指向实例的指针,而不是类本身。我尝试将id
作为目标移交,但当我在某处调用[MyTargetClass class]
时,我仍然会收到“没有已知的类方法[...]”错误。当我调用[Facade someForwardedMethod]
时,我得到一个“No visible @interface [...]声明选择器[...]”错误。
当然我知道我还需要覆盖[[Facade sharedInstance] someForwardedMethod]
和respondsToSelector:
,因此我的代码如下所示:
methodSignatureForSelector:
有没有办法让这项工作成功,还是我必须选择另一种方法?
我可以通过带有
的facade实例在我的目标类中调用一个类方法- (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];
}
}
这比我希望的有点丑,但它确实有效。但是,当我处理facade类时,我无法在目标类中调用类方法。为了使编译器安静,我可以编写
[(id)[Facade sharedInstance] doSomethingClassyInTargetClass];
然后它会在运行时抛出'NSInvalidArgumentException'“[Facade doSomethingClassyInTargetClass]:无法识别的选择器发送到类[...]”。显然,Facile类的类方法在不尊重[(Class)[Facade class] doSomethingClassyInTargetClass];
的情况下得到解决,但毕竟前面有一个forwardInvocation:
......
答案 0 :(得分:5)
啊,当你添加“No visible @interface [...]声明选择器[...]”时,一切都清楚了。
考虑一个只有实例方法的简单形式。您实现-forwardInvocation:
,然后您有这样的代码:
[obj doSomething];
现在,MyObject
实际上并未在其标题中声明doSomething
,即使它会响应它。所以编译器抱怨这可能不起作用。解决方法是:
[(id)obj doSomething];
当您声明某些内容id
时,编译器会停止检查目标是否实际实现了选择器。但是 no 接口也可以声明doSomething
。然后编译器再次怀疑它可能是一个错字,并给你一个警告。并且通过“无接口”,我的意思是“没有编译器可以访问的接口。”编译器只能访问此编译单元中的接口(即.m文件及其包含的头文件)。因此,您需要确保包含定义此选择器的标头。
现在,对于课程,您无法使用id
,但您应该可以使用Class
来实现相同的目标,例如:
[(Class)MyClass doSomethingClassy];
如果可能想要查看来自iOS的RNObserverManager示例代码:PTL第4章,它演示了类似的内容。
你也应该看看Ken Thomases引用的forwardTargetForSelector:
。
您无需将sharedInstance
投射到id
。只需更改签名,因为您总是希望以这种方式使用它:
+ (id)sharedInstance;