2

我在我的应用程序中使用GCDAsyncSocket( CocoaAsyncSocket) 进行套接字通信。由于 的异步特性GCDAsyncSocket,我的网络请求(submitMessage如下)与接收数据时运行的回调块(socket:didReadData)分离。

- (void)submitMessage:(NSDictionary *)messageObject onCompletion:(completionBlock)block {
    ...
    [_socket writeData:requestData withTimeout:self.timeout tag:0];
    [_socket readDataToLength:4 withTimeout:self.timeout tag:TAG_HEADER];
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    ...
    NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    if (self.completionBlock != nil)
        self.completionBlock(responseObject);
    }
}

这种方法适用于一次性交易。但是在某些情况下,我需要发布一个请求,然后使用接收到的数据,发布另一个请求。我无法让它正常工作。基本上,我需要这样的东西:

[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
    (...callback 1...)
    }];
    [self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
        (...callback 2...)
    }];
}];

或者

[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
    (...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
    (...callback 2...)
}];

严格的顺序是 request1 - callback1 - request2 - callback2。

那么问题来了,如何在第一个请求回调后阻止第二个请求运行呢?GCD(?dispatch_sync)会是要走的路吗?

编辑

我最终使用了类似于@tigloo 建议的解决方案(因此接受了他的回答),但使用NSCondition了代替GCD(如果有人对细节感兴趣,我遵循了这个很棒的讨论)。我已经在运行多个线程(主线程中的 UI,另一个线程中的高级套接字通信,以及第三个线程中的套接字操作)。设置类属性并使用NSCondition锁定GCDAsyncSocket委托直到响应到达似乎是最干净的方法。

4

2 回答 2

2

我想你几乎在那里。关于什么

[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
    // here, do something with response1 and create request2...
    // then you can make request2 directly at the end of the callback:
    [self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
        // here, do something with response2...
    }];
}];

不需要 GCD 指令,不需要阻止执行(无论如何这是一个不好的做法)。这能解决你的问题吗?

于 2013-04-23T09:06:27.453 回答
1

最简单的方法是将您的请求附加到串行调度队列,然后使用 dispatch_sync() 等待它们完成。可以在这里找到关于 StackOverflow 的讨论

实现它的实际方式取决于您的喜好。一个可能的想法如下:

  • 创建一个新类“SyncRequest”
  • 理想情况下,此类具有 bool 类型的私有属性“requestFinished”,在类的 init 方法中初始化为 NO
  • 在诸如“sendSyncRequest”之类的方法中,您调用 submitMessage:completionBlock:
  • 完成块将“requestFinished”属性设置为YES
  • “sendSyncRequest”的最后一行是 dispatch_sync(syncRequestQueue, ^(void){while(!requestFinished);});

这样,您可以构造多个 SyncRequest 实例,每个实例处理一个同步请求。粗略的实现:

@interface SyncRequest
@property bool requestFinished;
@end

@implementation SyncRequest

dispatch_queue_t syncRequestQueue;    

-(id)init
{
   self = [super init];
   if ( !self )
      return nil;

   self.requestFinished = NO;
   syncRequestQueue = dispatch_queue_create("com.yourid.syncrequest", DISPATCH_QUEUE_SERIAL);

   return self;
}

-(void) sendSyncRequest:(NSDictionary*)messageObject
{
   // submit message here and set requestFinished = YES in completion block

   // wait for completion here
   dispatch_sync(syncRequestQueue, ^(void){while(!self.requestFinished);});
}

@end

注意:我在没有编译器的情况下编写了代码,您可能必须在 dispatch_sync 调用中创建对“self”的间接引用以避免循环引用。

于 2013-04-22T15:30:33.827 回答