Синхронная связь с использованием GCDAsyncSocket

Я использую 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...)
}];

где порядок строго запрос1 - обратный вызов1 - запрос2 - обратный вызов2.

Итак, вопрос в том, как я могу заблокировать запуск второго запроса после обратного вызова первого запроса? Было бы GCD (dispatch_sync?) Будет ли путь?

редактировать

В итоге я использовал решение, похожее на то, что предложил @tigloo (следовательно, принял его ответ), но использовал NSCondition вместо GCD (если кого-то интересуют подробности, я следил за этой замечательной дискуссией). Я уже запускаю несколько потоков (пользовательский интерфейс в основном, высокоуровневые сокеты в другом потоке и операции с сокетами в третьем потоке). Установка свойства класса и использование NSCondition заблокировать GCDAsyncSocket делегировать до получения ответа кажется самым чистым подходом.

2 ответа

Решение

Самый простой подход - добавить ваши запросы в очередь последовательной отправки, а затем дождаться их завершения с помощью dispatch_sync(). Обсуждение Stackru можно найти здесь.

Фактический способ его реализации зависит от ваших предпочтений. Возможная идея заключается в следующем:

  • Создать новый класс "SyncRequest"
  • Этот класс в идеале имеет закрытое свойство типа bool "requestFinished", инициализированное в NO в методе init класса
  • В таком методе, как "sendSyncRequest", вы вызываете submitMessage: завершение блока:
  • Блок завершения установит для свойства 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

ПРИМЕЧАНИЕ. Я написал код, не имея под рукой компилятора. Возможно, вам придется создать косвенную ссылку на "self" в вызове dispatch_sync, чтобы избежать циклической ссылки.

Я думаю, что вы были почти там. Как насчет

[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, нет необходимости блокировать выполнение (что в любом случае является плохой практикой). Решает ли это вашу проблему?

Другие вопросы по тегам