문제

It appears that Apple recommends using runloops for data communications with an external accessory. However, unless I am missing something, runloops are not very suitable for certain types of communications.

We have an -experimental- accessory, to which we need to send an arbitrary number of bytes (up to, say 1024), which is followed by the accessory processing that data (variable delay, say between 1ms to 1000ms), followed by a variable length response (upto 1024 bytes) from the accessory.

We would like to develop a static library (framework) for communicating with the accessory. Basically, this library will have a function, which takes an NSArray or NSMutableArray as an input, and returns NSArray or NSMutableArray, containing the response.

The problem is that the recommended strategy of runloops isn't very suitable for this type of application. In the static library function, after preparing the data to be transmitted and scheduling the transmission, we have to enter some sort of "wait" state. However, this wait state can not be based on a polling method (such as waiting on a -synchronized- variable to be set by the receive routing), because then the receive routine never gets to execute (since they are on the same thread).

If we don't use runloops, then we can not know when to read the data, because we don't know when the data will be arriving.

Any ideas or recommendations on how to approach this problem? Are there any examples out there?

도움이 되었습니까?

해결책

This is not a runLoop or ExternalAccessories problem. This is a daily OOP problem.

The best way is to create a Communication object that can write to the outputStream and wait for response. Use @protocols to do this! (Event-Listener driven procedure)

TRY THIS:

First of all you have to attach input/output streams to a runLoop:

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

Become their delegate:

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

Once you become delegate you have to implement this method:

-(void)stream:handleEvent:{};

This is the command to write out data to a stream:

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

This is an example code, (once you have session created, whereas the answer we expect is a byte):

in 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;

In Comm.m:

@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

Hope this helps!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top