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