Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ChristianVirtual

macrumors 601
Original poster
May 10, 2010
4,122
282
日本
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:

Code:
   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:
   //
   // 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
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.