WatchOS:来自扩展的UI更新是否应该在主线程上调用?

24

对于iOS应用程序,UI更新仅会在主线程中完成 - 不这样做不被建议且可能导致意外行为。

在watchOS中,操作系统以手表扩展和应用程序作为不同的“容器”进行结构化。通常,UI更新是从扩展中调用的,并且这些更新会更新应用程序容器中的某些内容。

是否适用相同的主线程逻辑来自手表扩展更新UI,或者可以从后台调用UI更新?


编辑 - 为了带来一些清晰度。从应用程序容器中,UI更新应该在主线程上发生(如大多数系统/操作系统中所发生的那样,如下所示)。问题实际上是watchOS是否为我们处理了这个问题,即在扩展的后台线程上调用UI更新是否会自动发布到应用程序容器的主线程上。


你是在问它们应该在主线程上被调用还是可以在后台线程上被调用吗? - tktsubota
@TroyT 两个语句对我来说似乎是等价的,但我想“应该”语句更加正确。如果某些操作不应该在后台被调用,那我就不会这样做。基本上 - “iOS中的相同主线程UI逻辑是否适用于watchOS?” - Jordan Smith
3个回答

4

苹果公司的watchOS应用程序编程指南可能是权威指南,但我在其中找不到关于在主线程以外的线程上执行UI更新的参考。

如果从主线程调用UI更新很重要,那么人们会认为它会在某个地方明确说明(就像在iOS应用程序编程指南中,在线程和并发部分中所做的那样):

涉及视图、核心动画和许多其他UIKit类的工作通常必须在应用程序的主线程上执行。当然,也有一些例外情况,例如基于图像的操作通常可以在后台线程上执行,但是如果有疑问,请假设工作需要在主线程上进行。
尽管如此,上面的引用也适用于Watch扩展的UI更新,因为它运行在iOS上。
以上所有内容都是为了说明,我认为没有任何苹果文档说明这方面的问题。
另一个数据点是:苹果的Lister示例代码现在包括一个WatchKit扩展,在我简要研究它时,它似乎正在将获取分派到后台队列(请参见ListInfo.swift:34),并通过分派回主队列来更新UI(ListsInterfaceController.swift:98)。甚至还有一条注释说它正在这样做:

fetchInfoWithCompletionHandler(_:) 方法在后台队列上调用其完成处理程序,然后返回到主队列以进行 UI 更新。

根据上述内容,我认为最好在主线程上进行更新,除非您确定这样做会对性能或其他方面产生影响。


很好,那是一些更加扎实的逻辑 - 不幸的是,有两件事我还不完全相信:从watchOS 2开始,扩展程序不再在iOS上运行,而是在watchOS上运行 - 这与你遵循iOS逻辑的规则相矛盾。其次 - 我相信列表示例代码使用了该调用不仅仅是在手表扩展程序上,这就是为什么它需要回调到主线程 - 代码可能是作为iOS应用程序的一部分运行的。我暂时不会给出奖励。 - Jordan Smith
你在现阶段谨慎行事的观点是我唯一完全认同的!虽然感谢你的回答。至于我想知道的原因,我只是不想不必要地回调主线程50次。这可能会导致界面更新稍晚一些,虽然不是什么大问题,但每一点响应性都很重要。 - Jordan Smith
考虑到您的答案被自动选为悬赏答案,我认为您应该编辑一下,去掉任何不正确的陈述。如果找到更确定的答案之前,我很乐意暂时将其标记为正确答案。 - Jordan Smith

2

在通过技术支持事件联系苹果后,收到了以下答复和解释。

简而言之:使用主线程。

All updates should be done from the main thread. This has always been the general recommendation for UIKit and that recommendation extends to watchOS.

It might be helpful to understand the underlying reason for this requirement. Keep in mind that, even with a centralized communication channel to serialize changes, many problems arise when you attempt to manipulate UI state from background threads. For example, while the serialization channel can prevent multiple UI commands from attempting to simultaneously execute, it can’t control the order in which unrelated commands will execute. Consider the following 2 blocks:

block 1 {     
  DoUIChange1     
  DoUIChange2     
}

block 2 {     
  DoUIChange3     
  DoUIChange4     
}

If both blocks are executed on the main thread, then the actual command stream is either:

DoUIChange1   
DoUIChange2   
DoUIChange3   
DoUIChange4

or…

DoUIChange3   
DoUIChange4   
DoUIChange1   
DoUIChange2

However, if both blocks are executed on their own threads, even more possibilities open up:

DoUIChange3   
DoUIChange1   
DoUIChange2   
DoUIChange4

or..

DoUIChange1   
DoUIChange3   
DoUIChange2   
DoUIChange4

or..

DoUIChange1   
DoUIChange3   
DoUIChange4   
DoUIChange2

etc…

Needless to say, if the UI code is at all complex the number of combinations quickly becomes enormous, making unexpected UI bugs basically unavoidable.


-1

你应该始终在主线程上进行UI更新。否则,将导致UI渲染变慢或潜在的应用程序崩溃。这不仅适用于iOS或watchOS,几乎每种编程语言(C#,Java,C ++等)都要求您在主线程上进行UI更新。

在watchOS 1中,您所建议的可能是有道理的,因为扩展位于iPhone上,而UI位于手表上。在这种情况下,应用程序确实作为两个单独的进程运行,并且您“可能”不需要调度到主线程以进行UI更新。但在watchOS 2中,情况就不同了。尽管watchOS扩展和UI具有不同的目标,但在watchOS 2中,它们不会在手表上作为单独的进程运行(您可以通过在Xcode中查看Apple Watch上运行的进程来验证这一点,并查看每个应用程序只有一个进程)。仅仅因为它被打包为两个单独的容器(甚至签名不同)并不意味着它们在手表上作为两个单独的进程运行。


我明白你的意思,但我不同意/不认为这是一个可以接受的答案。首先,手表应用程序和扩展程序存在于完全不同的容器中。它们几乎不知道彼此的存在。你怎么知道它们是同一个进程? - Jordan Smith
当您从后台扩展线程调用UI更新时,如何知道watchOS不会在应用程序容器中调用主线程UI更新呢?问题实际上并不是通常是否应该在主线程上进行UI更新,这是一个已知的事实。相反,从“扩展”中,它应该在主线程上完成吗?还是由我们处理,我们可以从后台线程调用而无需为每个UI更新跳回到主线程? - Jordan Smith
首先,进程之间的跳转会增加明显的时间延迟。其次,当您的iPhone已连接并且手表可达时(调试->附加到进程),您可以将Xcode附加到正在运行的进程上。您会注意到只有扩展进程可用于附加。第三,由于在异步调用后未返回到UI线程(主要是健康包查询),我在watchOS上遇到了许多UI错误和崩溃。 - lehn0058
好的,感谢您的澄清。但是我仍然不同意您的一些观点,抱歉,请让我解释一下。仅仅因为您无法将调试器附加到应用程序容器进程上,并不意味着所有内容都在您在扩展中使用的同一线程上运行。您可能并不知道,有一些系统进程会加载watchOS应用程序容器,并且WatchKit扩展API会将UI调用传递给它。 - Jordan Smith
关于UI异步错误 - 我不太相信。我目前在我的watchOS应用程序中做了一些,它“似乎”运行良好。你的崩溃可能是由于没有返回主线程引起的Core Data线程问题,或其他类型的线程问题,而不是与更新UI有关。我在我参与的项目中看到许多这些类型的线程问题,其中Core Data是最突出的 - 不总是与UI相关。 - Jordan Smith
抱歉如果我看起来有点严厉。我正在寻找一个明确的答案,因为我正在处理的东西被很多人使用 - 得确保正确性。你的回答只是缺乏我认为对于“明确”的答案必要的可靠证据,在这个阶段我可以想到很多原因可能会证明答案是不正确的。如果您能提供可靠的证据或文档,那么我很乐意接受您的答案。 - Jordan Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接