ChatGPT解决这个技术问题 Extra ChatGPT

如何在swift中使用后台线程?

如何在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." } }

t
tobiasdm

斯威夫特 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


如果有人想要更类似于 Swift 的语法,我创建了 Async,它为 Async.background {} 之类的语法添加了一些糖
我在 xCode 6.0.1 和 ios 8 中使用您的代码。它作为“QOS_CLASS_BACKGROUND”返回类给出错误,它是 UInt32 类型,“dispatch_get_global_queue”需要第一个参数作为 int,所以类型错误即将到来。
因此,在 Xcode 6.1.1 中,仅使用简单的“QOS_CLASS_BACKGROUND”不会出错。是固定的吗?
@LucasGoossen 是的,它已被修复。我已经相应地更新了帖子。
@NikitaPronchik 答案不是很清楚吗?否则,请随时对其进行编辑。
f
frouo

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 闭包就会被调用。
c
cromir

最佳实践是定义一个可以多次访问的可重用函数。

可重复使用的功能:

例如,像 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
    })

不错的片段,应该是正确的答案。 @戴尔克利福德
出色的高级现代 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)。
N
Naresh

在 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 线程上执行任何操作。它只是将它堆叠起来完成,之后,其他正在完成的项目都完成了
F
Fattie

斯威夫特 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 的一些深入信息。


说。由于您的答案是最好的,所以我输入了一行代码,显示您如何“完成后回电”。随意放松或编辑,干杯
a
alex

来自Jameson Quave's tutorial

斯威夫特 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})

只是为了澄清,为什么要使用它而不是接受的答案?这只是一个较旧的 API 吗?
@Sirens 我认为这对于支持 < iOS 8 的应用程序非常有用。
我将它用于 iOs 8.2 来强制进程。
DISPATCH_QUEUE_PRIORITY_DEFAULT 恢复为 QOS_CLASS_DEFAULT。所以我想你可以说它是更高级/可接受的语法。
DispatchQueue 不是线程。
H
Hadži Lazar Pešić

斯威夫特 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)
     }
}

X
Xys

斯威夫特 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
    }
}

A
AlBlue

您必须将要在后台运行的更改与要在 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 及以下版本吗?
C
Cosmin

由于上面已经回答了 OP 问题,我只想添加一些速度注意事项:

我不建议以 .background 线程优先级运行任务,尤其是在 iPhone X 上,任务似乎分配在低功耗内核上。

以下是计算密集型函数的一些真实数据,该函数从 XML 文件(带缓冲)读取并执行数据插值:

设备名称/.background/.utility/.default/.userInitiated/.userInteractive

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.8 s

请注意,并非所有设备的数据集都相同。它在 iPhone X 上是最大的,在 iPhone 5s 上是最小的。


C
Community

虽然答案很好,但无论如何我想分享我的面向对象解决方案 Up to date for 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 时,您可以将这些类型设置为您需要传入和传出后台任务的任何类型,但如果您不需要这些类型,则可以将其标记为未使用,只需将其设置为:Void 或使用更短的语法: ()

执行异步任务时,会经过 3 个步骤:

beforeTask:()->Void 在任务执行之前在 UI 线程上调用。 backgroundTask: (param:BGParam)->BGResult 在 afterTask:(param:BGResult)->UI 线程上调用后立即在后台线程上调用,结果来自后台任务


这对我来说非常有用。干得好,为什么不把它放在github上?
V
Viral Savaliya

多用途线程函数

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
}

r
rougeExciter

我真的很喜欢 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()
            }
        }
    }
}

A
Anil Dhameliya

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


C
Craig D

下面的代码是否有缺点(当需要在之后启动前景屏幕时)?

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)
        }
     }
}   

更简单???...
t
tobiasdm
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})

a
ackers

在 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 中创建实际线程的示例,但很难找到任何东西。 GCD 不适合我的用例,我发现这个小例子很有帮助。