Mark Brackett
Mark Brackett

Reputation: 85675

dealloc on Background Thread

Is it an error to call dealloc on a UIViewController from a background thread? It seems that UITextView (can?) eventually call _WebTryThreadLock which results in:

bool _WebTryThreadLock(bool): 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.

Background: I have a subclassed NSOperation that takes a selector and a target object to notify.

-(id)initWithTarget:(id)target {
   if (self = [super init]) {
      _target = [target retain];
   }
   return self;
}

-(void)dealloc {
   [_target release];
   [super dealloc];
}

If the UIViewController has already been dismissed when the NSOperation gets around to running, then the call to release triggers it's dealloc on a background thread.

Upvotes: 5

Views: 5705

Answers (4)

Gabriele Mondada
Gabriele Mondada

Reputation: 516

Yes, it is an error to make a UIViewController releasing in a background thread (or queue). In UIKit, dealloc is not thread safe. This is explicitly described in Apple's TN2109 doc:

When a secondary thread retains the target object, you have to ensure that the thread releases that reference before the main thread releases its last reference to the object. If you don't do this, the last reference to the object is released by the secondary thread, which means that the object's -dealloc method runs on that secondary thread. This is problematic if the object's -dealloc method does things that are not safe to do on a secondary thread, something that's common for UIKit objects like a view controller.

However, it's quite hard to respect this rule and if you put precondition(Thread.isMainThread) in all your view controller dealloc/deinit(), you probably would notice very weird (and hard to fix) cases in which this rule is not respected.

It seems that Apple is aware of this fragility and is moving away from this rule. In fact, when you annotate a class with @MainActor, everything is ensured to be executed in the Main Thread, except deinit(). In Swift 6, the compiler prevents you from calling main actor code from deinit().

Even if in the past we have been asked to ensure deallocation in the main thread, in the future we will be asked to ensure that deallocation can happen in any thread.

Upvotes: 11

Jason
Jason

Reputation: 1433

The second post here had useful info, but their answer didn't work for me.

UIWebView in multithread ViewController

It also seems that this may have been addressed in iPhone OS 4, but not sure.

I ended up not releasing my UIWebView in the controller's dealloc when [NSThread isMainThread] was NO. Would rather leak than crash (until I get a better solution).

Upvotes: 3

drawnonward
drawnonward

Reputation: 53689

It is an error to call dealloc on anything at any time. You should only ever call release.

You should not access any UI related instances from a background thread. This includes using getter methods because they may modify things internally. However, retain and release are thread safe for any object at any time, as long as the normal rules for retain and release are followed. UI related instances include any object that is referenced by an active UIView or UIViewController.

performSelectorOnMainThread does not do anything more than retain an object until it gets to the main thread. It is safe to call on any UI related object.

Upvotes: 2

David Gelhar
David Gelhar

Reputation: 27900

The simple rule is that it's an error to do anything on a UI* from a background thread.

Upvotes: 6

Related Questions