如何在swift中使用后台线程?
Posted
技术标签:
【中文标题】如何在swift中使用后台线程?【英文标题】:How to use background thread in swift? 【发布时间】:2014-07-26 05:04:13 【问题描述】:如何在swift中使用线程?
dispatchOnMainThread:^
NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));
];
【问题讨论】:
转换哪个部分有问题? 为什么最后一行的分号前有]
?
如果您能解释一下您遇到的问题或您需要帮助的地方,将会很有帮助。
如果它真的对你有帮助,你必须接受正确的答案,它也会帮助其他人找到正确的解决方案。
DispatchQueue.global(qos: .background).async print("Run on background thread") DispatchQueue.main.async print("We finished that.") // only back on the main thread, may you access UI: label.text = "Done."
【参考方案1】:
斯威夫特 3.0+
在 Swift 3.0 中有很多 modernized。在后台队列上运行看起来像这样:
DispatchQueue.global(qos: .userInitiated).async
print("This is run on a background queue")
DispatchQueue.main.async
print("This is run on the main queue, after the previous code in outer block")
Swift 1.2 到 2.3
let qualityOfServiceClass = QOS_CLASS_USER_INITIATED
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue,
print("This is run on a background queue")
dispatch_async(dispatch_get_main_queue(), () -> Void in
print("This is run on the main queue, after the previous code in outer block")
)
)
Pre Swift 1.2 – 已知问题
从 Swift 1.1 开始,Apple 不支持上述语法,无需进行一些修改。传递QOS_CLASS_USER_INITIATED
并没有实际工作,而是使用Int(QOS_CLASS_USER_INITIATED.value)
。
欲了解更多信息,请参阅Apples documentation
【讨论】:
我在 xCode 6.0.1 和 ios 8 中使用您的代码。它给出错误为“QOS_CLASS_BACKGROUND”返回类,它是 UInt32 类型,“dispatch_get_global_queue”需要第一个参数作为 int,所以类型错误是来了。 所以在 Xcode 6.1.1 中我没有因为使用简单的“QOS_CLASS_BACKGROUND”而出错。修复了吗? @LucasGoossen 是的,它已被修复。我已经相应地更新了帖子。 @NikitaPronchik 答案不是很清楚吗?否则,请随时对其进行编辑。【参考方案2】:Dan Beaulieu 在 swift5 中的回答(也适用于 swift 3.0.1)。
斯威夫特 5.0.1
extension DispatchQueue
static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil)
DispatchQueue.global(qos: .background).async
background?()
if let completion = completion
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute:
completion()
)
用法
DispatchQueue.background(delay: 3.0, background:
// do something in background
, completion:
// when background job finishes, wait 3 seconds and do something in main thread
)
DispatchQueue.background(background:
// do something in background
, completion:
// when background job finished, do something in main thread
)
DispatchQueue.background(delay: 3.0, completion:
// do something in main thread after 3 seconds
)
【讨论】:
太棒了,感谢您更新到 Swift 3.0.1 格式! 我使用扩展的次数比任何活着的人都多。但是使用与原版完全不同的扩展程序存在真正的危险! @Frouo 非常优雅,是否可以在 4 个异步调用全部完成时添加完成处理程序?我知道这有点跑题了。 是的,忘记那个链接了。你所需要的只是一个调度组——非常非常简单;完全不用担心! @DilipJangid 你不能,除非你在background
闭包中的工作非常非常长(~=无限)。此方法将持续有限时间:您的后台作业需要执行的时间。因此,一旦您的后台作业执行时间+延迟过去,completion
闭包就会被调用。【参考方案3】:
最佳实践是定义一个可以多次访问的可重用函数。
可重用功能:
例如像 AppDelegate.swift 这样的全局函数。
func backgroundThread(_ delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil)
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0))
background?()
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue())
completion?()
注意:在 Swift 2.0 中,将上面的 QOS_CLASS_USER_INITIATED.value 替换为 QOS_CLASS_USER_INITIATED.rawValue
用法:
A.在后台运行一个延迟 3 秒的进程:
backgroundThread(3.0, background:
// Your background function here
)
B.要在后台运行进程,然后在前台运行完成:
backgroundThread(background:
// Your function here to run in the background
,
completion:
// A function to run in the foreground when the background thread is complete
)
C.延迟 3 秒 - 注意使用没有背景参数的完成参数:
backgroundThread(3.0, completion:
// Your delayed function here to be run in the foreground
)
【讨论】:
nice sn-p,应该是正确的答案。 @戴尔克利福德 出色的高级现代 Swift-y 方法可从低级 C 库访问老式 GCD 方法。应该成为 Swift 的标准。 非常好。请您确认一下,延迟仅适用于完成块。所以这意味着A.中的延迟没有影响,后台块立即执行,没有延迟。 您应该可以将if(background != nil) background!();
替换为background?()
以获得更快捷的语法?
能否请您为 Swift 3 更新此内容?自动转换器将其转换为DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async
,但这会引发类似cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'
的错误。找到了一个可行的解决方案here (DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async
)。【参考方案4】:
Swift 3 版本
Swift 3 利用新的DispatchQueue
类来管理队列和线程。要在后台线程上运行一些东西,你会使用:
let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async
print("Run on background thread")
或者如果你想要两行代码:
DispatchQueue.global(qos: .background).async
print("Run on background thread")
DispatchQueue.main.async
print("We finished that.")
// only back on the main thread, may you access UI:
label.text = "Done."
您还可以在this tutorial 中获取有关 Swift 3 中 GDC 的一些深入信息。
【讨论】:
说。由于您的答案是最好的,所以我输入了一行代码,显示您如何“完成后回电”。随意放松或编辑,干杯【参考方案5】:在 Swift 4.2 和 Xcode 10.1 中
我们有三种类型的队列:
1.主队列: 主队列是由系统创建并与应用程序主线程关联的串行队列。
2。全局队列: 全局队列是一个并发队列,我们可以根据任务的优先级进行请求。
3.自定义队列:可由用户创建。自定义并发队列始终通过指定服务质量属性 (QoS) 映射到全局队列之一。
DispatchQueue.main//Main thread
DispatchQueue.global(qos: .userInitiated)// High Priority
DispatchQueue.global(qos: .userInteractive)//High Priority (Little Higher than userInitiated)
DispatchQueue.global(qos: .background)//Lowest Priority
DispatchQueue.global(qos: .default)//Normal Priority (after High but before Low)
DispatchQueue.global(qos: .utility)//Low Priority
DispatchQueue.global(qos: .unspecified)//Absence of Quality
这些队列可以通过两种方式执行
1.同步执行
2。异步执行
DispatchQueue.global(qos: .background).async
// do your job here
DispatchQueue.main.async
// update ui here
//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async
// Perform task
DispatchQueue.main.async
// Update UI
self.tableView.reloadData()
//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0)
//Here call your function
//If you want to do changes in UI use this
DispatchQueue.main.async(execute:
//Update UI
self.tableView.reloadData()
)
来自 AppCoda:https://www.appcoda.com/grand-central-dispatch/
//This will print synchronously means, it will print 1-9 & 100-109
func simpleQueues()
let queue = DispatchQueue(label: "com.appcoda.myqueue")
queue.sync
for i in 0..<10
print("?", i)
for i in 100..<110
print("Ⓜ️", i)
//This will print asynchronously
func simpleQueues()
let queue = DispatchQueue(label: "com.appcoda.myqueue")
queue.async
for i in 0..<10
print("?", i)
for i in 100..<110
print("Ⓜ️", i)
【讨论】:
最佳线程教程medium.com/@gabriel_lewis/… 当您使用.background
QoS 或 .userInitiated
时,我没有看到任何变化,但对我来说,使用 .background
时效果很好
您可能看不到使用 .background 和 .userInitiated QoS 之间的任何区别,因为系统可以覆盖您的设置并将 .background QoS 提升为 .userInitiated。这是从主 UI 队列中使用的队列的幕后优化,以使它们与父级的 QoS 匹配。您可以使用 Thread.current.qualityOfService 检查当前线程的 QoS。
DispatchQueue.main.async 根本不会立即在 UX 线程上执行任何操作。它只是将要完成的事情堆叠起来,之后,其他正在完成的项目都完成了【参考方案6】:
来自Jameson Quave's tutorial
斯威夫特 2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
//All stuff here
)
【讨论】:
只是为了澄清,为什么要使用它而不是接受的答案?这只是一个较旧的 API 吗? @Sirens 我认为这对于支持 我将它用于 iOs 8.2 来强制进程。 DISPATCH_QUEUE_PRIORITY_DEFAULT 恢复为 QOS_CLASS_DEFAULT。所以我想你可以说它是更高级/可接受的语法。 DispatchQueue 不是线程。【参考方案7】:Swift 4.x
把它放在某个文件中:
func background(work: @escaping () -> ())
DispatchQueue.global(qos: .userInitiated).async
work()
func main(work: @escaping () -> ())
DispatchQueue.main.async
work()
然后在需要的地方调用它:
background
//background job
main
//update UI (or what you need to do in main thread)
【讨论】:
【参考方案8】:斯威夫特 5
为方便起见,创建一个包含以下内容的文件“DispatchQueue+Extensions.swift”:
import Foundation
typealias Dispatch = DispatchQueue
extension Dispatch
static func background(_ task: @escaping () -> ())
Dispatch.global(qos: .background).async
task()
static func main(_ task: @escaping () -> ())
Dispatch.main.async
task()
用法:
Dispatch.background
// do stuff
Dispatch.main
// update UI
【讨论】:
【参考方案9】:您必须将要在后台运行的更改与要在 UI 上运行的更新分开:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
// do your task
dispatch_async(dispatch_get_main_queue())
// update some UI
【讨论】:
所以dispatch_async(dispatch_get_main_queue()) // update some UI
在后台语句(外块)执行完毕时被调用?
这不只适用于 Swift 2.3 及以下版本吗?【参考方案10】:
由于上面已经回答了 OP 问题,我只想添加一些速度注意事项:
我不建议以 .background 线程优先级运行任务,尤其是在任务似乎分配在低功耗内核上的 iPhone X 上。
以下是来自计算密集型函数的一些真实数据,该函数从 XML 文件(带有缓冲)读取并执行数据插值:
设备名称/.background/.utility/.default/.userInitiated/。用户交互
-
iPhone X:18.7s / 6.3s / 1.8s / 1.8s / 1.8s
iPhone 7:4.6s / 3.1s / 3.0s / 2.8s / 2.6s
iPhone 5s:7.3s / 6.1s / 4.0s / 4.0s / 3.8s
请注意,并非所有设备的数据集都相同。它在 iPhone X 上是最大的,在 iPhone 5s 上是最小的。
【讨论】:
【参考方案11】:虽然答案很好,但无论如何我想分享我的面向对象解决方案最新的 swift 5。
请查看:AsyncTask
受 android 的 AsyncTask 的概念启发,我用 Swift 编写了自己的类
AsyncTask 可以正确且轻松地使用 UI 线程。此类允许执行后台操作并在 UI 线程上发布结果。
这里有几个使用示例
示例 1 -
AsyncTask(backgroundTask: (p:String)->Void in//set BGParam to String and BGResult to Void
print(p);//print the value in background thread
).execute("Hello async");//execute with value 'Hello async'
示例 2 -
let task2=AsyncTask(beforeTask:
print("pre execution");//print 'pre execution' before backgroundTask
,backgroundTask:(p:Int)->String in//set BGParam to Int & BGResult to String
if p>0//check if execution value is bigger than zero
return "positive"//pass String "poitive" to afterTask
return "negative";//otherwise pass String "negative"
, afterTask: (p:String) in
print(p);//print background task result
);
task2.execute(1);//execute with value 1
它有 2 种泛型类型:
BGParam
- 执行时发送给任务的参数类型。
BGResult
- 后台计算结果的类型。
当您创建 AsyncTask 时,您可以将这些类型设置为您需要传入和传出后台任务的任何类型,但如果您不需要这些类型,您可以将其标记为未使用,只需将其设置为:@987654326 @ 或更短的语法:()
当一个异步任务执行时,它会经过 3 个步骤:
beforeTask:()->Void
在任务执行前在 UI 线程上调用。
backgroundTask: (param:BGParam)->BGResult
之后立即在后台线程上调用
afterTask:(param:BGResult)->Void
在 UI 线程上调用,结果来自后台任务
【讨论】:
这对我来说非常有用。干得好,为什么不放到github上呢?【参考方案12】:线程的多用途函数
public enum QueueType
case Main
case Background
case LowPriority
case HighPriority
var queue: DispatchQueue
switch self
case .Main:
return DispatchQueue.main
case .Background:
return DispatchQueue(label: "com.app.queue",
qos: .background,
target: nil)
case .LowPriority:
return DispatchQueue.global(qos: .userInitiated)
case .HighPriority:
return DispatchQueue.global(qos: .userInitiated)
func performOn(_ queueType: QueueType, closure: @escaping () -> Void)
queueType.queue.async(execute: closure)
像这样使用它:
performOn(.Background)
//Code
【讨论】:
【参考方案13】:我真的很喜欢 Dan Beaulieu 的回答,但它不适用于 Swift 2.2,我认为我们可以避免那些讨厌的强制解包!
func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil)
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0))
background?()
if let completion = completion
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue())
completion()
【讨论】:
【参考方案14】:Grand Central Dispatch 用于处理我们 iOS 应用程序中的多任务处理。
你可以使用这个代码
// Using time interval
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1)
print("Hello World")
// Background thread
queue.sync
for i in 0..<10
print("Hello", i)
// Main thread
for i in 20..<30
print("Hello", i)
更多信息请使用此链接:https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html
【讨论】:
【参考方案15】:dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0),
// Conversion into base64 string
self.uploadImageString = uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
)
【讨论】:
【参考方案16】:下面的代码是否有缺点(需要在之后启动前景屏幕时)?
import Foundation
import UIKit
class TestTimeDelay
static var connected:Bool = false
static var counter:Int = 0
static func showAfterDelayControl(uiViewController:UIViewController)
NSLog("TestTimeDelay", "showAfterDelayControl")
static func tryReconnect() -> Bool
counter += 1
NSLog("TestTimeDelay", "Counter:\(counter)")
return counter > 4
static func waitOnConnectWithDelay(milliseconds:Int, uiViewController: UIViewController)
DispatchQueue.global(qos: .background).async
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(milliseconds), execute:
waitOnConnect(uiViewController: uiViewController)
)
static func waitOnConnect(uiViewController:UIViewController)
connected = tryReconnect()
if connected
showAfterDelayControl(uiViewController: uiViewController)
else
waitOnConnectWithDelay(milliseconds: 200, uiViewController:uiViewController)
【讨论】:
更简单???.......【参考方案17】:在 Swift 4.2 中这有效。
import Foundation
class myThread: Thread
override func main()
while(true)
print("Running in the Thread");
Thread.sleep(forTimeInterval: 4);
let t = myThread();
t.start();
while(true)
print("Main Loop");
sleep(5);
【讨论】:
我希望这没有被否决。我一直在网上搜索有关如何在没有 GCD 的情况下在 Swift 中创建实际 threads 的示例,但很难找到任何东西。 GCD 不适合我的用例,我发现这个小例子很有帮助。以上是关于如何在swift中使用后台线程?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Swift 的后台线程中从 CoreData 中获取未保存的数据?