Блокировка связи с аксессуаром ios

Похоже, что Apple рекомендует использовать циклы выполнения для передачи данных с внешним аксессуаром. Однако, если я что-то не упустил, циклы выполнения не очень подходят для определенных типов связи.

У нас есть -экспериментальный-аксессуар, которому нам нужно отправить произвольное количество байтов (скажем, до 1024), за которым следует аксессуар, обрабатывающий эти данные (переменная задержка, скажем, от 1 мс до 1000 мс), за которым следует Ответ переменной длины (до 1024 байт) от аксессуара.

Мы хотели бы разработать статическую библиотеку (фреймворк) для связи с аксессуаром. По сути, эта библиотека будет иметь функцию, которая принимает NSArray или NSMutableArray в качестве входных данных и возвращает NSArray или NSMutableArray, содержащие ответ.

Проблема в том, что рекомендуемая стратегия циклов выполнения не очень подходит для этого типа приложений. В статической библиотечной функции после подготовки данных для передачи и планирования передачи мы должны войти в своего рода состояние «ожидания». Однако это состояние ожидания не может быть основано на методе опроса (например, ожидание синхронизированной переменной, которая будет установлена ​​маршрутизацией приема), поскольку в этом случае подпрограмма приема никогда не будет выполняться (поскольку они находятся в одном потоке). .

Если мы не используем циклы выполнения, мы не можем знать, когда считывать данные, потому что мы не знаем, когда данные поступят.

Любые идеи или рекомендации о том, как подойти к этой проблеме? Есть ли примеры?


person SomethingBetter    schedule 04.11.2013    source источник


Ответы (1)


Это не проблема runLoop или ExternalAccessories. Это ежедневная проблема ООП.

Лучший способ — создать объект связи, который может писать в outputStream и ждать ответа. Используйте @protocols для этого! (процедура, управляемая прослушивателем событий)

ПОПРОБУЙТЕ ЭТО:

Прежде всего, вам нужно подключить потоки ввода/вывода к runLoop:

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

Станьте их делегатом:

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

Как только вы станете делегатом, вы должны реализовать этот метод:

-(void)stream:handleEvent:{};

Это команда для записи данных в поток:

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

Это пример кода (после создания сеанса, тогда как ответ, который мы ожидаем, представляет собой байт):

в Comm.h:

/* 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.h:

@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

Надеюсь, это поможет!

person Tommaso Resti    schedule 14.11.2013
comment
Но в вашем примере вы планируете потоки в «currentRunLoop», вы не планируете их в другом потоке. Операции чтения и записи, по-видимому, также выполняются в основном потоке. - person andrea.rinaldi; 26.08.2015
comment
цикл выполнения запускается во время простоя потока и не блокируется... В этом случае, когда байт становится доступным, запускается уведомление (NSStreamEventHasBytesAvailable). Я могу прочитать байт с помощью '[stream read:&ch maxLength:1];'. Эта операция выполняется так же быстро, как чтение байта из вектора. Да, конечно, байт читается в основном потоке, но не блокируется. - person Tommaso Resti; 26.08.2015
comment
Спасибо за ответ! В любом случае, я не указал причину своего комментария, извините: P Я не говорю, что вы ошибаетесь или у вашего решения есть проблемы, а просто комментарий «Теперь вы можете уведомить об этом делегата (ваш основной поток) ' немного вводит в заблуждение, потому что вы уже находитесь в основной теме :) - person andrea.rinaldi; 26.08.2015