Apple’s UIKit is generally designed to be dealt with from within the main thread – operating with it from another thread can result in unpredictable results, including:
- Everything may be fine
- The user interface element may not be redrawn after your property changes
- UIKit will give you a warning in the console, and throw an exception
- The application will crash
- The application won’t crash, and the UI will be left in an undefined state
The question then becomes: how can we update our user interface from a background thread? Utilising Apple’s Grand Central Dispatch APIs is one such way.
Suppose one of your UIViewControllers receives a message to update the background color of its main view. This method is called from a background thread (say, when a particular network packet arrives). We can attach ourselves to the main thread, make the call, and then return to our background thread so we can continue to process other network packets.
- ( void )updateBackgroundColor:(UIColor *)color
{ dispatch_sync(dispatch_get_main_queue(), ^{
self .view.backgroundColor = color;
});
} |
Taking a closer look at this, we have:
dispatch_sync(dispatch_queue_t queue, ^( void )block)
|
The “sync” part of the function name (as differentiated from “async”) means that the code that comes after the dispatch_sync call (and in this case, the code which sent the -updateBackgroundColor: message) will not continue to run until the work inside the block is done. This can be useful where the code that follows makes the assumption that the work inside the block has been done (e.g. if it were then to query the backgroundColor property of the view later on). Making the “async” version of the call throws the work to be done on a queue, and processes it later on, when the specified queue gets its chance to run again. It looks similar to the synchronous version:
1
|
dispatch_async(dispatch_queue_t queue, ^( void )block)
|
Many of the dispatch_* functions operate on dispatch queues. Here, we are operating on the main queue, which is connected with the main run loop (our UI thread):
dispatch_get_main_queue() |
What about if we want to be able to call the same method, but from the UI thread? This creates a problem! The dispatch_sync function places its block of work to be done at the end of the main dispatch queue. It will then block until that work has been done. However, we’re already in the main thread, and until we return to the run loop, the block won’t be scheduled to run. This creates a deadlock – something we want to avoid. We can fix this with the following modification:
- ( void )updateBackgroundColor:(UIColor *)color
{ if ([ NSThread isMainThread])
{
self .view.backgroundColor = color;
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
self .view.backgroundColor = color;
});
}
} |
This is a little ugly, and duplicates the code – we can change it like this:
- ( void )updateBackgroundColor:(UIColor *)color
{ dispatch_block_t work_to_do = ^{
self .view.backgroundColor = color;
}
if ([ NSThread isMainThread])
{
work_to_do();
}
else
{
dispatch_sync(dispatch_get_main_queue(), work_to_do);
}
} |
Even better, we can throw this into a function which can be used elsewhere:
void RUN_ON_UI_THREAD(dispatch_block_t block)
{ if ([ NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
} |
so that our code is simplified to:
- ( void )updateBackgroundColor:(UIColor *)color
{ RUN_ON_UI_THREAD(^{
self .view.backgroundColor = color;
});
} |
相关推荐
`Updating Form from Another Thread without Creating Delegates for Every Type of Update`这个主题探讨的是如何在不为每种类型的更新创建单独委托的情况下,从其他线程安全地更新UI。 通常,当你尝试在非UI线程...
在神经网络中更新权重是深度学习模型训练过程中的核心步骤,这一过程直接影响着模型的性能和学习能力。本文将深入探讨“更新神经网络权重”的概念、相关算法以及其在实际应用中的重要性。 首先,神经网络的权重表示...
RayWenderlich 最新...Discover the new features for developers in iOS 11, such as ARKit, Core ML, Vision, drag & drop, document browsing, the new cha nges in Xcode 9 and Swift 4 — and much, much more.
[2] , ranging from web servers to games, programming languages and most of the application types that are in use on modern computers. Ports will be discussed further in the section The Ports ...
The data is stored in one place and the index is stored in another. Pointers indicate the storage location of the indexed items in the underlying table. In a nonclustered index, the leaf level ...
The examples use time-tested code from working applications. What You'll Learn: How to create database and database applications using iOS and Swift How to insert, select, edit, and delete records ...
在iOS开发中,文件操作是不可或缺的一部分,尤其是在创建、读取、修改和删除应用程序内部或外部存储的数据时。本文将深入探讨iOS中的文件系统以及如何进行文件操作。 首先,了解iOS的文件系统至关重要。iOS使用沙盒...
更新复杂结构的有限元模型是结构工程领域的一个重要问题,由于复杂结构分析的有限元模型(FEM)规模庞大,使用传统方法进行模型修正不仅耗时而且成本高昂,甚至在某些情况下是不可行的。因此,基于Kriging预测模型的...
当我们在 Spring 容器中存在多个同一接口或父类的实现时,如果不明确指定要注入哪个实现,Spring 就会抛出 "Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or ...
The goal is to inherit a new `Game` class from `Panel` (specifically, a `Canvas`) so it can be included in the WPF visual tree. The visual bounds of the panel will become the viewing area for the XNA...
from your web sites—even the default installation shown in Figure 1-1 shows how pleasing a Joomla web site can look. Adding content or updating the design of your entire web site is a snap . . . and ...
The first edition of this book was released in 2001 at the same time Microsoft released the Beta 2 build of .NET 1.0. Working on that first edition was certainly a challenge, given that the APIs and ...
-完全可定制的UI -无需服务器端代码:普通的HTTP服务器就足够了 -顺序和非顺序补丁处理 -更新的最短路径计算 -通过完全修复触发检测非常老的版本 -自我更新功能 管理工具 -版本管理 -补丁管理 -启动更新管理 - CI /...