Maybe I mix too many different technologies together and run in some roadblock; some advise would be much appreciated.
I have an app which connects to several server; each connection with one input and output socket stream. The connection goes to a defined port and is close to telnet protocol. Text input/output. quite simple.
First I have an openStream function as wrapper called from main thread which create a client-specific GDC queue and dispatch the input/output-stream creation within that queue asynchronously:
gcdQueue = dispatch_queue_create([self.client.hostName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
// possible priorities:
// DISPATCH_QUEUE_PRIORITY_HIGH
// DISPATCH_QUEUE_PRIORITY_DEFAULT
// DISPATCH_QUEUE_PRIORITY_LOW
if (self.runASync)
{
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_async(gcdQueue, ^{
[self openStreamsInternal];
});
}
Code for the technical open of steams
...
//
// in openStreamsInternal()
//
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.client.hostName, [self.client.hostPort intValue], &_readStream, &_writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)_readStream;
self.outputStream = (__bridge_transfer NSOutputStream *)_writeStream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
self.runLoop = [NSRunLoop currentRunLoop];
[self.inputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
// ... some lines later
if (self.runASync && (self.inputStream || self.outputStream))
{
[self.runLoop run];
}
I open both socket streams and link them to a runloop within the GCD-queue (assuming it will indirectly create a thread; not sure if that is always guaranteed).
Then via the delegate (a member function of my connection class) for the streams in
Code:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
I do what I have to do with in data flooding in. No issues until I stay in foreground. I close the streams when I go into background to release the resources.
Now with iOS 7 I want to enable background refresh for the streams. For that i don't close the streams anymore when moving into background and have the notification code in
Code:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(@"called in background for data fetch");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (Connection *connection in self.document.clientList)
{
// Add a task to the group
[connection parseResponseInQueue:group];
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"finished with background for data fetch");
completionHandler(UIBackgroundFetchResultNewData);
}
This is one of my variants of background processing; not working well. This one supposed to wait a second and check if the input stream has data copied. If thats the case the parser would be called and the method comes to and end; removing one item from the dispatch group created in the iOS7 background app notification.
I don't like the dispatch_after as it seems very brute; but without I run in an endless loop as the streams seems not be triggered at all times.
Code:
- (void)parseResponseInQueue:(dispatch_group_t)group
{
if (gcdQueue != nil)
{
dispatch_group_async(group, gcdQueue, ^{
while ([self.data length] > 0)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), gcdQueue, ^{
NSLog(@"%@, wait for parser in background", self.client.hostName);
});
}
#if 0
if ([self.data length] > 0)
{
NSLog(@"%@, start working on buffer %d from background", self.client.hostName, [self.data length]);
[self parseResponse];
NSLog(@"%@, finish working on buffer, left %d in background", self.client.hostName, [self.data length]);
// NSLog(@"data : %@", [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
}
else
{
NSLog(@"%@, no data for background processing", self.client.hostName);
}
#endif
});
}
}
But somehow I don't get the refresh done. Sometimes the completionHandler finish without any update and sometime my dispatch group never finish.
So my question is mainly:
1) what is your suggestion to combine background app refresh with multiple streams in GCD queues.
2) does those runloops still be active when I trigger in background
3) are the GCD queues still active
4) should I better schedule in one runloop for all client connection in addition to one main runloop ?
Somehow I need to fresh thoughts on the way forward.
TIA