Monday, January 9, 2012

Using NSRunLoop and NSURLConnection


For my book Core Objective-C in 24 Hours I provided example code that demonstrates the use of NSURLConnection objects for downloading URL resources.  Now I believe that it’s a good time to review the NSRunLoop API and how it is used to manage input sources.
As noted in the Foundation Framework Reference, an NSRunLoop object process input for sources such as mouse and keyboard events from the window system, NSPort objects, and NSConnection objects.  Now NSURLConnection objects are commonly used to perform asynchronous loading of data via its delegate methods.  By default, for the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.  So, how do you use an NSURLConnection instance and an NSRunLoop object to asynchronously download URL resources?
A run loop can be thought of as an event-processing loop running on a single thread, with input sources (and code [i.e. callback methods] to be executed if the input is received) registered on the loop.  Hence if an NSURLConnection object is registered on a run loop, input from this object (i.e. data loaded from the connection) will cause the corresponding (NSURLConnection delegate) callback methods to be executed.  Therefore, we can use these APIs to asynchronously download a URL resource with the following code fragment:
  // Setup and start connection to resource (urlString)
  NSURL *url = [NSURL URLWithString:urlString];
  NSMutableData *resourceData = [[NSMutableData alloc] init];
  NSURLRequest *request = [[NSURLRequest alloc]
                initWithURL:url
                cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
                timeoutInterval:10];
  NSURLConnection *urlConnection = [[NSURLConnection alloc]
                   initWithRequest:request
                   delegate:self];

  // Attach connection to run loop to receive callbacks for async download
  NSRunLoop *loop = [NSRunLoop currentRunLoop];
  [loop run];
Note that this code may loop forever, even if the NSURLConnection input source is removed.  If you want to guarantee that you exit the run loop after you have finished downloading the resource you can update the code with a conditional statement as follows (the boolean value isLoaded, set to true by the NSURLConnectionDelegate callback method when the URL has finished loading) :
  // Attach connection to run loop to receive callbacks for async download
  NSRunLoop *loop = [NSRunLoop currentRunLoop];
  while ((!isLoaded) &&
         ([loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate
           distantFuture]]))
  {
  }
The same approach can be used for other NSRunLoop input sources.  In summary the steps to connecting an input source to a run loop are:
1.     Create the input source.
2.     Connect the callback methods to the input source.
3.     Start the input source.
4.     Connect the input source to the run loop. 
References:

2 comments:

  1. we can still receive the delegate call backs if we don't write
    // Attach connection to run loop to receive callbacks for async download
    NSRunLoop *loop = [NSRunLoop currentRunLoop];
    [loop run];

    How, does NSURLConnection is attached with this code?

    ReplyDelete