我有一个外观单例,我想将一些类方法调用转发给一些"静态"类.
乍一看,forwardInvocation:似乎是一个可行的解决方案,然而,NSInvocation的invokeWithTarget:而且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:,但毕竟它有一个-前面的...
啊,当你添加"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的参考资料.
你不必投sharedInstance来id.只需更改签名,因为您总是希望以这种方式使用它:
+ (id)sharedInstance;
Run Code Online (Sandbox Code Playgroud)