背景:
我有一个对象(我们称之为BackendClient)代表了与服务器的连接。它的方法生成为单个@protocol,并且它们都是同步的,因此我想创建代理对象,该对象将在后台调用它们。主要问题是返回值,我显然不能从异步方法返回它,因此我需要传递一个回调。 “简单”的方法是复制所有BackendClient的方法并添加回调参数。但这不是解决该问题的动态方法,而ObjectiveC本质是动态的。那就是performSelector:出现的地方。它完全解决了问题,但是几乎杀死了代理对象的透明性。

问题:
我希望能够将未声明的选择器发送到代理(NSProxy的子类)对象,就好像它已经声明过一样。
例如,我有方法:

-(AuthResponse)authByRequest:(AuthRequest*)request

BackendClient协议中。我希望代理 call 如下所示:
[proxyClient authByRequest:myRequest withCallback:myCallback];

但这不会编译,因为

“BackendClientProxy”没有可见的@interface声明选择器“authByRequest:withCallBack:”

好。让我们稍微放松一下编译器:
[(id)proxyClient authByRequest:myRequest withCallback:myCallback];

Awww。另一个错误:

选择器'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 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

call 者:
 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/

10-12 06:19