问题
Drawing/painting should always be done on the GUI thread otherwise deadlocks can occur!...
How do you determine if a call may cause unsafe painting off of the main thread for iOS?
The problem is we get stacks like this which are not on the main thread...
#19 0x02a788d2 in -[CALayer drawInContext:]
#20 0x02a784b0 in backing_callback
#21 0x02a77d52 in CABackingStoreUpdate
#22 0x02a7701d in -[CALayer _display]
#23 0x02a76ac7 in CALayerDisplayIfNeeded
#24 0x02a689e1 in CA::Context::commit_transaction
#25 0x02a68732 in CA::Transaction::commit
#26 0x02aa604f in CA::Transaction::release_thread
#27 0x918b21e3 in _pthread_tsd_cleanup
#28 0x918b1df6 in _pthread_exit
#29 0x0018bbf2 in +[NSThread exit]
#30 0x0018bb5b in __NSThread__main__
#31 0x918a981d in _pthread_start
#32 0x918a96a2 in thread_start
How do you find out if you're going to cause this? Are there any debugging tips or other techniques to alert yourself when you do something wrong.
回答1:
I was surprised to find that setNeedsDisplay doesn't automatically queue the drawing onto the main thread....
It seems that when I use performSelectorOnMainThread:@selector(setNeedsDisplay) from the background thread it doesn't produce a stack like above and paints on the main thread as it should.
Maybe there is a good reason why it doesn't queue re-draws on the main thread when calling setNeedsDisplay from a background thread. I'm considering either going through all of my code and making sure setNeedsDisplay is called from the main thread or whether it is possible to create a category for UIView:setNeedsDisplay which will check if the current thread is the main thread before calling setNeedsDisplay otherwise it will re-run setNeedsDisplay on the main thread.
DISCLAIMER... I didn't read that this is how it is working I found this by testing.
回答2:
Before manipulating anything that can cause some drawing, you should make sure you're on the main thread.
For instance if you want to reload a UITableView from a detached thread, you should call:
[myTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
And if you have several statements that can cause drawing, you can have a separate method that do the drawing and make sure it's performed on the main thread:
- (void) asyncDoSomeStuff {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
...
// Update UI
[self performSelectorOnMainThread:@selector(refreshGUI) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void) refreshGUI {
[myTableView reloadData];
[myImageView setImage:[UIImage imageNamed:@"blabla.png"]];
...
}
And lastly if you're unsure about which thread is going to call a particular method that perform some drawing, then at the beginning of the method you can add the following statement, which will ensure that the whole method is going to execute on the main thread:
- (void) myMethod {
if(![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO];
return;
}
// Do your stuff
}
Otherwise I don't think there is a solution to detect that some drawing is going to be performed outside of the main thread. For instance, anything related to UIKit should occur in the main thread.
来源:https://stackoverflow.com/questions/3637454/deadlocks-how-do-you-determine-if-a-call-may-cause-unsafe-painting-off-of-the-m