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

标签 ios objective-c thrift nsproxy

背景: 我有一个代表与服务器连接的对象(我们称之为 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/

相关文章:

ios - 在 UITableView 中搜索具有多个部分的 Controller

ios - 打开语音时未调用 didSelectRowAtIndexpath

objective-c - 如何重用 UIView 和方法 - iOS 中的 DRY 代码

hadoop - Hadoop无法启动Thrift服务器,Hue无法与Hadoop NameNode和DataNode通信

ios - appdelegate 中的 UIButton 外观层不起作用

ios - AVAudioPlayer 缓慢降低级别

ios - 在通知服务扩展和主应用程序之间共享数据

iphone总是返回UIInterfaceOrientationPortrait

c++ - 与 Thrift Hook

hadoop - 创建外部表时Hive报错(state=08S01,code=1)