使用GCDAsyncSocket进行同步通信

时间:2013-04-22 09:21:47

标签: ios objective-c grand-central-dispatch cocoaasyncsocket gcdasyncsocket

我在我的应用中使用GCDAsyncSocketCocoaAsyncSocket)进行套接字通信。由于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。

所以问题是,如何在第一个请求回调后阻止第二个请求运行? GCDdispatch_sync?)会成为可行的方法吗?

修改

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

2 个答案:

答案 0 :(得分: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指令,不需要阻止执行(无论如何这都是一种不好的做法)。这会解决您的问题吗?

答案 1 :(得分:1)

最简单的方法是将您的请求附加到串行调度队列,然后使用dispatch_sync()等待它们完成。 A discussion on StackOverflow can be found here

实施它的实际方式取决于您的偏好。可能的想法如下:

  • 创建一个新类“SyncRequest”
  • 此类理想情况下具有bool“requestFinished”类型的私有属性,在类'init method
  • 中初始化为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”的间接引用,以避免循环引用。