14

I am new to objective C, coming from .NET and java background.

So I need to create some UIwebviews asynchronously, I am doing this on my own queue using

     dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
     dispatch_async(queue, ^{
        // create UIwebview, other things too
             [self.view addSubview:webView];
        });

as you owuld imagine this throws an error :

   bool _WebTryThreadLock(bool), 0xa1b8d70: Tried to obtain the web lock from a thread other  
   than the main thread or the web thread. This may be a result of calling to UIKit from a  
   secondary thread. Crashing now...

So how can I add the subview on the main thread?

Huang
  • 1,355
  • 2
  • 11
  • 28

3 Answers3

17

Since you are already using dispatch queues. I wouldn't use performSelectorOnMainThread:withObject:waitUntilDone:, but rather perform the subview addition on the main queue.

dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
dispatch_async(queue, ^{
    // create UIwebview, other things too

    // Perform on main thread/queue
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.view addSubview:webView];
    });
});

It is fine to instantiate the UIWebView on a background queue. But to add it as a subview you must be on the main thread/queue. From the UIView documentation:

Threading Considerations

Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

Florian
  • 5,326
  • 4
  • 26
  • 35
  • beware, even initWithFrame is not guaranteed to be thread safe. see http://stackoverflow.com/questions/11122957/is-it-ok-to-create-a-uiview-on-a-background-thread – stefreak Feb 12 '15 at 17:53
  • Maybe my understanding of 'thread-safe' is wrong. But I think thread-safe means that an object can be manipulated from several threads simultaneously. E.g. you could think of a thread-safe array, that would let you add objects from multiple threads. However, in this case we are not manipulating or accessing the view object from multiple threads/queues simultaneously. We are creating it on one thread/queue and then essentially handing it over to another one. – Florian Feb 12 '15 at 19:00
  • it turns out the answer to the question I linked was misleading - sorry. Apple says `The only time this may not be strictly necessary is when creating the view object itself` so it should be safe. – stefreak Feb 12 '15 at 20:41
  • My app is woken up by the OS in background mode. I need to add a subview to the `keyWndow` based on a certain user location. Will this be enough for my usecase? https://stackoverflow.com/questions/46584566/how-to-stop-handle-ui-operations-in-ios-when-app-moves-to-background-state – nr5 Oct 06 '17 at 07:47
2

Most UIKit objects, including instances of UIView, must be manipulated only from the main thread/queue. You cannot send messages to a UIView on any other thread or queue. This also means you cannot create them on any other thread or queue.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • 5
    That's not entirely correct. It is totally fine to instantiate a view in the background. From the UIView documentation: Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread. – Florian Feb 24 '13 at 07:48
  • @Florian Is it right to use method addSubview in background, if both views not interact with UI? – Bohdan Savych Jun 12 '17 at 12:22
  • @BohdanSavych I am not sure about that. What is your use case? – Florian Jun 16 '17 at 06:40
1

As rob said the UI changes should be done only on main thread.You are trying to add from a secondary thread.Change your code [self.view addSubview:webView]; to

[self.view performSelectorOnMainThread:@selector(addSubview:) withObject:webView waitUntilDone:YES];

kaar3k
  • 994
  • 7
  • 15