NSThread和UIViewController的交互
如果我生成一个新线程,然后在其中使用这样的代码将一个新的控制器推到我的UINavigationController上。
(a)不工作
-(void)myCallbackInThread { // move on... UIApplication* app = [UIApplication sharedApplication]; [app changeView]; }
那么我发现该视图出现了,但是没有响应用户输入。
如果我这样更改代码
(b)工作
-(void)myCallbackInThread { // move on... UIApplication* app = [UIApplication sharedApplication]; [app performSelectorOnMainThread:@selector(moveToMain) withObject:nil waitUntilDone:FALSE]; }
然后,一切正常。
有什么暗示吗?
解决方案
刚刚在iPhone线程文档中找到了
If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but it also has the advantage of simplifying the logic for managing your user interface.
我仍然看不到什么会真正导致显示内容,但无法接收用户输入,但是以后我会遵循该指南。
正如文档所述,"如果我们不确定某个特定的图形操作,请计划从主线程执行此操作。"
遵循的一个很好的经验法则是,如果未明确将类记录为线程安全的,则可能不是。此外,如前所述,未记录为线程安全的代码在被多个线程使用时可能不会很快失败,但可能只会表现出未定义的行为。
在情况下,它实际上取决于[app changeView]中发生的事情,但是它停止响应的原因很可能是新辅助线程上没有运行循环调度事件(请参见下文)。但是,一般而言,从辅助线程更新GUI是一个非常糟糕的主意。正如我们已经发现的那样,所有这些事件都应该通过主线程。
第二个示例起作用而不是第一个示例起作用的主要原因是,UIApplication在主线程上为我们设置并处理了运行循环和事件分发程序。因此,当我们调用performSelectorInMainThread时,选择器将分派到主运行循环,该循环可以处理gui输入和其他事件。事件分派器也由UIApplication在主线程上运行和管理。
因此,基本上,不要在辅助线程上执行任何GUI管理活动。将它们分配到主线程。而且,如果我们需要在辅助线程上进行处理(用于计时器或者异步调用等),则必须在该线程上启动和管理自己的运行循环(有关管理运行循环的更多信息,请参见NSRunLoop)。
UIKit或者AppKit中的UI代码几乎都不是线程安全的。它的失败方式是无关紧要的,因为如果我们担心它的失败方式,那么我们正在做的事情将导致各种奇怪的错误,无论如何,这些错误都会在不同的OS版本之间发生细微变化。
我最好的建议是除非文档说它是安全的,否则不要使用后台线程中的东西。