-1

I ran into an issue today where my app was failing to start in time. It turns out some third-party code I was using was trying to get the user agent string using the trick below:

-(NSString*)userAgentString
{
    webView = [[UIWebView alloc] init];
    webView.delegate = self;
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"www.google.com"]]];

    // Wait for the web view to load our bogus request and give us the secret user agent.
    while (self.userAgent == nil) 
    {
        // This executes another run loop. 
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    return self.userAgent;
}

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    self.userAgent = [request valueForHTTPHeaderField:@"User-Agent"];

    // Return no, we don't care about executing an actual request.
    return NO;
}

(from http://blog.sallarp.com/iphone-ipad-get-user-agent-for-uiwebview/#more-1003)

Prior to calling that code, I had queued some operations by adding them to the queue returned by +[NSOperationQueue mainQueue]. These operations are intended to be executed in the background as they make calls to +[NSData dataWithContentsOfURL:].

Once the method on the run loop is called, it executes my queued operation, blocking the main thread and preventing the app from launching. Right now I've gotten a workaround by making sure to not queue any operations until after the third-party code executes, but if anyone knows why this is happening, and how to prevent it in the future, I'd love to hear it. Thanks!

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
  • Looking a bit at the docs, it implies run loops in this mode process input sources available to them. I suppose a queued operation counts? But why would it not dispatch this off to a different thread? – Carl Veazey Mar 06 '12 at 19:39

1 Answers1

1

The why is fairly straightforward. You queued operations on the main queue. Then someone (this code) told the main queue to run operations. Your operations ran. This is one of the reasons you should almost never run the main runloop manually. It causes all kinds of weird things to happen.

As to how to prevent it: Don't pump the main runloop manually, and open bugs against libraries that do (including against Apple for running the main runloop in the middle of NSAttributedString initWithHTML:...).

I'm not clear why the calling code wants the user-agent string. It's not generally your business, but it's also fairly static, so should be determinable by other means (not the least of which is just hard-coding the answer). Without knowing the goal, I'm not certain what to replace this with, but this approach is not a good one.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Rob, thanks for your answer. Good to know that is not a good practice - I'd never actually seen it before myself. It's strange to me this would cause main thread execution of NSOperation, but I guess that's the strange behavior you mention :). As far as the third party code goes, I can't comment on their need for the user agent string. PS I love your book! – Carl Veazey Mar 06 '12 at 23:27