Erik B Erik B - 22 days ago 9
iOS Question

How can I get the main thread to sleep while waiting for a delegate to be called?

Consider a class with these methods:

- (id) initWithFrame: (CGRect) frame
{
if (!(self = [super init]))
return nil;
webView = [[UIWebView alloc] initWithFrame:frame];
[webView setDelegate:self];
lock = [[NSConditionLock alloc] initWithCondition:LOCK_WAIT];
return self;
}

- (void) setHTML: (NSString *) html
{
[lock lockWhenCondition:LOCK_WAIT];
[webView loadHTMLString:html baseURL:nil];
[lock unlock];
}

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{
[lock lockWhenCondition:LOCK_WAIT];
// Locking to be able to unlock and change the condition.
[lock unlockWithCondition:LOCK_GO];
}

- (NSString *) stringByEvaluatingJavaScriptFromString: (NSString *) jsCommand
{
[lock lockWhenCondition:LOCK_GO];
NSString * res = [webView stringByEvaluatingJavaScriptFromString:jsCommand];
[lock unlock];
return res;
}


Let's call this class SynchronousUIWebView.
From the main thread I execute:

webView = [[SynchronousUIWebView alloc] initWithFrame:frame];
[webView setHTML:html];
[webView stringByEvaluatingJavaScriptFromString:jsCommand];


The problem seems to be that the delegate is not called until I leave the current call stack, which I don't since I'm waiting for the delegate call to happen, aka deadlock. To me it seems like the delegate call is pushed to a queue that is called when the current call is done. So the question is can I modify this behavior?

Note: The reason this is needed is that I can't execute the JavaScript until the HTML has loaded.

Answer

You cannot use a UIWebView from another than the main thread. So you could leave out all your locks and just let the web view and the run loop handle your delegate calling. The delegate methods should then be called from the main thread, too.

The main-thread-only problem exists on a couple of Cocoa classes, including all of UIKit. It means, that all these classes' implementations rely on being called from the main thread only (for example, they use the main thread's run loop implicitly). It's simply not possible to use them on other threads, even when implementing your own locking.