2

내 응용 프로그램의 소켓 통신에 GCDAsyncSocket (CocoaAsyncSocket)을 사용하고 있습니다. GCDAsyncSocket의 비동기 속성으로 인해 네트워크 요청 (submitMessage 아래)이 데이터 수신시 실행되는 콜백 블록 (socket:didReadData)과 분리됩니다.GCDAsyncSocket을 사용한 동기식 통신

- (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 것과 유사한 솔루션 (따라서 그의 대답을 받아들이)를 사용하지만, GCD 대신 NSCondition를 사용하여 종료 (사람이 세부 사항에 관심이 있다면, 나는 this great discussion를 따라). 나는 이미 다중 쓰레드 (메인 쓰레드의 UI, 다른 쓰레드의 하이 레벨 소켓 통신, 세번째 쓰레드의 소켓 작업)를 실행 중이다. 응답이 도착할 때까지 클래스 속성을 설정하고 NSCondition을 사용하여 GCDAsyncSocket 대리자를 잠그는 것이 가장 깨끗한 접근 방법입니다.

+0

명확하고 간결하게 작성하십시오. 당신의 문제는 정확히 무엇입니까. (나는 당신의 글을 읽었지만 그 질문을 정확하게 지적하는 것은 어렵습니다.) – ilmiacs

+0

감사합니다 @ilmiacs, 그것이 더 합리적인 지보십시오. – lucianf

+0

그러면 완료 후 콜백 1에서'submitMessage : request2'를 왜 보지 않으시겠습니까? – ilmiacs

답변

1

가장 쉬운 방법은 직렬 발송 대기열에 요청을 추가 한 다음 dispatch_sync()를 사용하여 완료 될 때까지 기다리는 것입니다. A discussion on StackOverflow can be found here.

실제로 구현하는 방법은 사용자의 취향에 달려 있습니다. 가능한 아이디어는 다음과 같습니다

  • 새로운 클래스를 만듭니다 "SyncRequest"
  • 이 클래스는 이상적으로 init 메소드 '클래스에서 NO 초기화, bool 형식의 개인 재산 "requestFinished"했다
  • 에서 {동안
  • 완료 블록이 속성
  • syncRequestQueue ("sendSyncRequest"dispatch_sync 것이다 마지막 행 YES에 "requestFinished"로 설정되며,^(공극) completionBlock : 예컨대 "sendSyncRequest는"당신 submitMessage 전화 등의 방법 (! 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 통화에서 "자기"에 대한 간접 참조를 생성 할 수 있습니다.

+0

감사합니다. 게시 한 스레드를 이미 읽었습니다. 원칙적으로 GCD의 작동 방식을 알고 있습니다.이 특별한 경우에 GCD를 사용하는 방법을 알지 못합니다. 직렬 대기열에서 요청을 보내면 콜백이 실행될 때까지 어떻게 대기해야합니까? 요청은 비동기 (GCDAsyncSocket 작동 방식)이기 때문에 곧바로 반환됩니다. – lucianf

+0

나는 무슨 뜻인지 예를 추가했습니다. – tigloo

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 지시문이 필요하지 않으므로 실행을 차단할 필요가 없습니다 (어쨌든 나쁜 실행입니다). 이게 당신의 문제를 해결합니까?

+0

메인 블록 (request1)이 즉시 완료되지 않는 한 정상적으로 작동합니다. 예, 첫 번째 콜백 블록이 결국 실행되고 네, 두 번째 요청과 두 번째 콜백이 결국 실행됩니다. 하지만이 같은 일련의 그룹을 실행해야합니다 (request1-request2). 순차적으로 실행하지 않으면 소켓이 혼란 스러울 수 있습니다. 쓰기가 혼합되어 콜백의 읽기가 잘못된 데이터를 얻게됩니다. 말이된다? – lucianf

+0

어 ... 나는 당신의 구현이 그러한 문제가 발생하지 않도록 보장해야한다고 생각합니다. 혼란스러워지는 소켓이 아닙니다. 귀하의 프로토콜 구현입니다. 그래서 첫 번째 : 콜백은 데이터를 받았을 때 호출됩니다. 따라서 응답을받은 후에 request2가 호출되어 원래 질문에 대한 답변을 얻을 수 있습니다. 그리고 두 번째 : 당신의 의견은 이미 실행 중일 때 또 다른 요청 1을 호출하는 것이 효과가 없다고 제안합니다. (나는 동의한다.) 소켓에 쓰기를 차단하는 것이 하나의 접근 방법이 될 수는 있지만 추천할만한 것은 아니다. 혼동하지 않도록 프로토콜을 수정하는 것이 좋습니다. – ilmiacs

+0

그 때 당신은 무엇을 추천합니까? 기본적으로, 나는 그런 일련의 _send/receive/send/receive_ 교환으로 만들어진 "동기화"방법을 가지고있다. 동기화 된 방식으로 "_sync_"시퀀스를 시작해야합니다. 내 프로토콜을 향상시킬 수있는 방법을 알지 못합니다. – lucianf

관련 문제