1

背景:BackendClient我有一个代表与服务器连接 的对象(我们称之为)。它的方法生成为单个@protocol并且它们都是同步的,所以我想创建将在后台调用它们的代理对象。主要问题是返回值,我显然不能从异步方法返回,所以我需要传递一个回调。“简单”的方法是复制 allBackendClient的方法并添加回调参数。但这不是解决该问题的动态方式,而 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在运行时以某种方式使用所需的方法构造新的,但我不知道该怎么做。

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

4

2 回答 2

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

米:

 @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];
    }
 }];
 ....
于 2013-09-01T16:32:17.597 回答
0

要在运行时查找选择器,您可以使用NSSelectorFromString(),但在这种情况下,您应该继续导入所需的任何标头以获取-authByRequest:

于 2013-08-31T23:56:15.990 回答