1

绘图/绘画应始终在 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

你怎么知道你是否会导致这个?当你做错事时,是否有任何调试技巧或其他技术来提醒自己。

4

2 回答 2

1

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.

于 2011-02-26T09:48:52.573 回答
1

我惊讶地发现 setNeedsDisplay 不会自动将绘图排队到主线程中......

似乎当我从后台线程使用 performSelectorOnMainThread:@selector(setNeedsDisplay) 时,它不会像上面那样产生堆栈,而是在主线程上按应有的方式进行绘制。

当从后台线程调用 setNeedsDisplay 时,它没有在主线程上排队重新绘制可能是有充分理由的。我正在考虑通过我的所有代码并确保从主线程调用 setNeedsDisplay 或者是否可以为 UIView:setNeedsDisplay 创建一个类别,它将在调用 setNeedsDisplay 之前检查当前线程是否是主线程,否则它将在主线程上重新运行 setNeedsDisplay。

免责声明...我没有读到它是这样工作的,我通过测试发现了这一点。

于 2010-09-03T22:25:03.303 回答