如何在没有performSelector的情况下发送未声明的选择器:?

时间:2013-08-31 23:53:54

标签: ios objective-c thrift nsproxy

背景 我有一个对象(让我们称之为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所需的方法,但我不知道如何做到这一点。

结论:我需要抑制此编译错误。知道怎么做吗?

2 个答案:

答案 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:的声明