2013-11-04 1 views
3

Apple은 외부 액세서리와의 데이터 통신을 위해 런 루프 (runloops)를 사용할 것을 권장합니다. 그러나 뭔가를 놓치지 않으면 runloops는 특정 유형의 통신에 적합하지 않습니다.ios 액세서리와의 통신 차단

우리는 임의의 바이트 수 (최대 1024)를 보내야하며, 그 다음에 데이터를 처리하는 부속 장치 (가변 지연, 1ms에서 1000ms 사이라고 말함) , 액세서리의 가변 길이 응답 (1024 바이트까지)이 뒤 따른다.

액세서리와 통신하기위한 정적 라이브러리 (프레임 워크)를 개발하고 싶습니다. 기본적으로이 라이브러리에는 NSArray 또는 NSMutableArray를 입력으로 사용하여 응답을 포함하는 NSArray 또는 NSMutableArray를 반환하는 함수가 있습니다.

문제는 runloops의 권장 전략이 이러한 유형의 응용 프로그램에 적합하지 않다는 것입니다. 정적 라이브러리 함수에서 전송할 데이터를 준비하고 전송을 예약 한 후에는 일종의 "대기"상태를 입력해야합니다. 그러나 수신 대기열은 결코 실행되지 않기 때문에 (같은 스레드에 있기 때문에) 수신 대기열이 폴링 메소드 (수신 라우팅에 의해 설정된 동기화 대기 변수와 같은 대기)와 같은 대기 상태를 기반으로 할 수 없습니다. .

우리는 runloops를 사용하지 않으면 데이터가 언제 도착할 지 모르기 때문에 데이터를 읽을시기를 알 수 없습니다.

이 문제에 접근하는 방법에 대한 아이디어 나 권장 사항이 있으십니까? 거기에 예제가 있습니까?

답변

3

이것은 runLoop 또는 ExternalAccessories 문제가 아닙니다. 이것은 매일 OOP 문제입니다.

가장 좋은 방법은 outputStream에 쓸 수 있고 응답을 기다릴 수있는 Communication 개체를 만드는 것입니다. 이렇게하려면 @protocols를 사용하십시오! (이벤트 리스너 구동 절차)

이 시도 : 모든

먼저 당신이 runLoop 입력/출력 스트림을 첨부해야한다 :

[[session outputStream] setDelegate:self]; 
[[session inputStream] setDelegate:self]; 
:

[[session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
[[session inputStream] open]; 
[[session outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
[[session outputStream] open]; 

자신의 대리인이 되십시오

일단 위임되면이 방법을 구현해야합니다.

(당신이 세션을 만든 후에 우리가 기대하는 대답은 바이트 인 반면,)

/* data is a NSData containing data to transmit. */ 
[[session outputStream] write:(uint8_t *)[data bytes] maxLength:[data length]]; 

이 예제 코드는 다음과 같습니다 :

-(void)stream:handleEvent:{}; 

이는 스트림에 데이터를 쓸 수있는 명령입니다 Comm.h에서

: Comm.m에서

/* Define your protocol */ 
@protocol CommDelegate <NSObject> 
    -(void)byteReceived: (char) byte; 
@end 

@interface Comm <NSObject> { 
    [...] 
    id<CommDelegate> delegate; 
} 
@end 

@property (nonatomic, retain) id<CommDelegate> delegate; 

:

@implementation Comm 

[...] 
-(id)init { 
    [...] 
    delegate = nil; 
    [...] 
} 

-(void)write: (NSData *) data { 
    [[session outputStream] write:(uint8_t *)[data bytes] maxLength:[data length]]; 
} 

-(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)_event { 
    switch (_event) 
    { 
     case NSStreamEventHasBytesAvailable: 
      /* This part will be executed every time your rx buffer contains at least 1 byte */ 
      switch(state) { 
       uint8_t ch; 
       /* Read byte per byte */ 
       [stream read:&ch maxLength:1]; 
       /* now ch contains a byte from your MFI device 
       ** and 'read' function decrease the length of the rx buffer by -1 */ 

       /* Now you can notify this to the delegate 
       */ 
       if(self.delegate != nil) 
        [delegate byteReceived: ch]; 
      } 
      break; 
    } 
} 

your_app_controller시간 :

@interface MyApp : UIViewController <CommDelegate> { 
    Comm comm; 
} 
@end 

your_app_controller.m :이 도움이

@implementation MyApp 

-(id)init { 
    [...] 
    comm = [[Comm alloc] init]; 
    [comm setDelegate: self]; /* Now your thread is listening your communication. */ 
} 

-(void)write { 
    byte out = 'X'; 
    [comm write: [NSData dataWithBytes: &out length: 1]]; 
} 

-(void)bytereceived:(char)reply { 
    if(reply == 'Y') { 
     [self write]; 
     //[self performSelectorInBackground:@selector(write) withObject:nil]; IT'S BETTER!!! 
    } 

} 

@end 

희망!

+0

하지만 'currentRunLoop'에서 스트림을 예약하면 다른 스레드에서 예약하지 않습니다. 읽기 및 쓰기 작업은 주 스레드에서도 실행되는 것으로 보입니다. –

+1

스레드의 유휴 시간에 "runloop"이 실행되고 차단되지 않습니다 ...이 경우 바이트를 사용할 수있게되면 알림이 시작됩니다 (NSStreamEventHasBytesAvailable). 나는 '[stream read : & ch maxLength : 1];'으로 바이트를 읽을 수있다. 이 연산은 벡터에서 바이트를 읽는 즉시 빠릅니다. 물론 바이트는 메인 쓰레드에서 읽히지 만 블로킹하지는 않습니다. –

+0

답변 해 주셔서 감사합니다! 어쨌든, 내 의견에 대한 이유를 밝히지 않았다. 미안 : P 나는 당신이 틀렸다고 말하거나 당신의 솔루션에 문제가 있다는 말은 아니지만 간단히 말해서 '이제 당신은 위임자에게 이것을 알릴 수있다. (주 스레드) '당신이 이미 메인 스레드에 있기 때문에 약간 오해의 소지가 있습니다 :) –