背景:
我有一个代表与服务器连接的对象(我们称之为 BackendClient
)。它的方法生成为单个 @protocol
并且它们都是同步的,所以我想创建将在后台调用它们的代理对象。主要问题是返回值,我显然不能从异步方法返回,所以我需要传递一个回调。 “简单”的方法是复制所有 BackendClient
的方法并添加回调参数。但这不是解决该问题的非常动态的方法,而 ObjectiveC 的本质是动态的。这就是 performSelector:
出现的地方。它完全解决了问题,但几乎扼杀了代理对象的透明度。
问题:
我希望能够将未声明的选择器发送到代理(NSProxy
的子类)对象,就像它已经被声明一样。
例如,我有方法:
-(AuthResponse)authByRequest:(AuthRequest*)request
在 BackendClient
协议(protocol)中。我希望代理调用看起来像这样:
[proxyClient authByRequest:myRequest withCallback:myCallback];
但这不会编译因为
No visible @interface for 'BackendClientProxy' declares the selector 'authByRequest:withCallBack:'
好的。让编译器冷静一下:
[(id)proxyClient authByRequest:myRequest withCallback:myCallback];
哇哦。另一个错误:
No known instance method for selector 'authByRequest:withCallBack:'
我想到的唯一一件事就是在运行时使用所需的方法以某种方式构造新的@protocol
,但我不知道该怎么做。
结论:我需要抑制这个编译错误。知道怎么做吗?
最佳答案
如果我理解的话,您有一个同步的、非线程的 API,您希望它是异步的,以便不阻塞,比如说,主事件循环等...
我会向 BackgroundClient 添加一个串行队列:
@property(strong) dispatch_queue_t serialQueue;
... somewhere in your -init ...
_serialQueue = dispatch_queue_create(..., serial constant);
然后:
- (void)dispatchOperation:(dispatch_block_t)anOperation
{
dispatch_async(_serialQueue, anOperation);
}
可以这样使用:
[myClient dispatchOperation:^{
[myClient doSynchronousA];
id result = [myClient doSynchronousB];
dispatch_async(dispatch_get_main_queue(), ^{
[someone updateUIWithResult:result];
}
}];
这是将 BackgroundClient 移至异步模型而无需重写或大量重构的最简单方法。
如果您想强化 API,请为 BackendClient 创建一个类包装器,其中包含一个客户端实例和串行队列。使所述类实例化客户端,而您的代码的其余部分仅从该包装器中检索实例。这将允许您仍然拥有相同的 dispatchOperation:
模型,但不需要镜像所有方法。
typedef void(^ AsyncBackendBlock(BackendClient* bc); @interface异步后端 +(instancetype)asyncBackendWithBackend:(BackendClient*)bc;
@property .... serialQueue;
- (void) dispatchAsync:(AsyncBackendBlock) backBlock;
@end
.m:
@interface AsyncBackend()
@property... BackendClient *client;
@end
@implementation AsyncBackend
- (void) dispatchAsync:(AsyncBackendBlock) backBlock
{
dispatch_async(_serialQueue, ^{
backBlock(_client);
});
}
@end
来电者:
AsyncBackend *b = [AsyncBackend asyncBackendWithBackend:[BackendClient new]];
[b dispatchAsync:^(BackendClient *bc) {
[bc doSomething];
id result = [bc retrieveSomething];
dispatch_async(dispatch_get_main_queue(), ^{
[uiThingy updateWithResult:result];
}
}];
....
关于ios - 如何在没有 performSelector :? 的情况下发送未声明的选择器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18553901/