背景
我有一个对象(让我们称之为BackendClient
)表示与服务器的连接。它的方法生成为单@protocol
并且它们都是同步的,所以我想创建将在后台调用它们的代理对象。主要问题是返回值,我显然无法从异步方法返回,所以我需要传递一个回调。 “简单”的方法是复制所有BackendClient
的方法并添加回调参数。但这不是解决该问题的非常动态的方式,而ObjectiveC本质是动态的。这就是performSelector:
出现的地方。它完全解决了问题,但它几乎杀死了代理对象的透明度。
问题:
我希望能够将未声明的选择器发送到代理(NSProxy
)对象的子类,就像它已经被声明一样。
例如,我有方法:
-(AuthResponse)authByRequest:(AuthRequest*)request
BackendClient
协议中的。我希望代理呼叫看起来像这样:
[proxyClient authByRequest:myRequest withCallback:myCallback];
但这不会编译,因为
'BackendClientProxy'没有可见的@interface声明选择器'authByRequest:withCallBack:'
行。让我们冷静下来编译一下:
[(id)proxyClient authByRequest:myRequest withCallback:myCallback];
噢。另一个错误:
没有已知的选择器'authByRequest实例方法:withCallBack:'
我唯一想到的就是在运行时以某种方式构建新的@protocol
所需的方法,但我不知道如何做到这一点。
结论:我需要抑制此编译错误。知道怎么做吗?
答案 0 :(得分:1)
如果我理解它,你有一个同步的,非线程的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 AsyncBackend +(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];
}
}];
....
答案 1 :(得分:0)
要在运行时查找选择器,您可以使用NSSelectorFromString()
,但在这种情况下,您应该继续导入所需的标头以获取-authByRequest:
的声明