绘图/绘画应始终在GUI线程上完成,否则会发生死锁!...
如何确定调用是否可能导致iOS主线程的不安全绘制?
问题是我们得到这样的堆栈,这些堆栈不在主线程上......
#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
你怎么知道你是否会引起这种情况? 当你做错事时,是否有任何调试技巧或其他技巧可以提醒自己。
答案 0 :(得分:1)
我很惊讶地发现setNeedsDisplay不会自动将绘图排队到主线程上....
似乎当我从后台线程使用performSelectorOnMainThread:@selector(setNeedsDisplay)时,它不会产生如上所述的堆栈,并且应该在主线程上绘制。
当从后台线程调用setNeedsDisplay时,可能有一个很好的理由说明它不会在主线程上重新绘制队列。我正在考虑要么经历我的所有代码并确保从主线程调用setNeedsDisplay,或者是否可以为UIView创建一个类别:setNeedsDisplay将在调用setNeedsDisplay之前检查当前线程是否是主线程,否则它将在主线程上重新运行setNeedsDisplay。
免责声明......我没有读到这就是它的工作原理我通过测试发现了这一点。
答案 1 :(得分:1)
在操作任何可能导致绘图的东西之前,你应该确保你在主线程上。
例如,如果要从分离的线程重新加载UITableView,则应调用:
[myTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
如果你有几个可能导致绘图的语句,你可以有一个单独的方法来完成绘图,并确保它在主线程上执行:
- (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"]];
...
}
最后如果你不确定哪个线程会调用执行某个绘图的特定方法,那么在方法的开头你可以添加以下语句,这将确保整个方法将要执行在主线上:
- (void) myMethod {
if(![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO];
return;
}
// Do your stuff
}
否则我不认为有一种解决方案可以检测到某些绘图是在主线程之外执行的。例如,与UIKit相关的任何事情都应该出现在主线程中。