Как использовать фоновый поток в Swift?
Как использовать потоки в Swift?
dispatchOnMainThread:^{
NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));
}];
17 ответов
Swift 3.0+
Многое было модернизировано в Swift 3.0. Запуск чего-либо в фоновом потоке выглядит следующим образом:
DispatchQueue.global(qos: .background).async {
print("This is run on the background queue")
DispatchQueue.main.async {
print("This is run on the main queue, after the previous code in outer block")
}
}
Свифт с 1.2 по 2.3
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
print("This is run on the 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_BACKGROUND
на самом деле не работает, вместо этого используйте Int(QOS_CLASS_BACKGROUND.value)
,
Для получения дополнительной информации см. Документацию Apple.
Дэн Болье ответ в swift3.
Swift 3.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
})
Рекомендуется определить функцию многократного использования, к которой можно обращаться несколько раз.
ВОЗМОЖНАЯ ФУНКЦИЯ:
например, где-то вроде 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)) {
if(background != nil){ background!(); }
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue()) {
if(completion != nil){ completion!(); }
}
}
}
Примечание: в Swift 2.0 замените QOS_CLASS_USER_INITIATED.value выше на QOS_CLASS_USER_INITIATED.rawValue вместо
ИСПОЛЬЗОВАНИЕ:
A. Чтобы запустить процесс в фоновом режиме с задержкой в 3 секунды:
backgroundThread(3.0, background: {
// Your background function here
})
Б. Чтобы запустить процесс в фоновом режиме, затем выполнить завершение на переднем плане:
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 4.2 и Xcode 10
//To call 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: {
})
Версия 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."
}
}
В этом руководстве вы также можете получить подробную информацию о GDC в Swift 3.
Swift 2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//All stuff here
})
Swift 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
}
}
Swift 4.1
Поместите это в какой-нибудь файл:
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)
}
}
Вы должны отделить изменения, которые вы хотите запустить в фоновом режиме, от обновлений, которые вы хотите запустить в пользовательском интерфейсе:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// do your task
dispatch_async(dispatch_get_main_queue()) {
// update some UI
}
}
Хорошие ответы, но в любом случае я хочу поделиться своим объектно-ориентированным решением, обновленным для swift 3.
пожалуйста, проверьте это: AsyncTask
Концептуально вдохновленный Android AsyncTask, я написал свой собственный класс в Swift
AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса.
Вот несколько примеров использования
Пример 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
вызывается в потоке пользовательского интерфейса непосредственно перед выполнением задачи.backgroundTask: (param:BGParam)->BGResult
вызывается в фоновом потоке сразу послеafterTask:(param:BGResult)->Void
вызывается в потоке пользовательского интерфейса с результатом из фоновой задачи
Поскольку на вопрос ОП уже был дан ответ, я просто хочу добавить некоторые соображения по поводу скорости:
Я не рекомендую запускать задачи с приоритетом потока .background, особенно на iPhone X, где задача, кажется, размещена на ядрах с низким энергопотреблением.
Вот некоторые реальные данные из вычислительно интенсивной функции, которая читает из файла XML (с буферизацией) и выполняет интерполяцию данных:
Имя устройства / .background / .utility / .default / .userInitiated / .userInteractive
- iPhone X: 18,7 с / 6,3 с / 1,8 с / 1,8 с / 1,8 с
- iPhone 7: 4,6 с / 3,1 с / 3,0 с / 2,8 с / 2,6 с
- iPhone 5s: 7,3 с / 6,1 с / 4,0 с / 4,0 с / 3,8 с
Обратите внимание, что набор данных не одинаков для всех устройств. Это самый большой на iPhone X и самый маленький на iPhone 5s.
Многоцелевая функция для потока
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
}
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
Есть ли недостаток (когда после этого нужно запустить экран переднего плана) в приведенном ниже коде?
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)
}
}
}
Мне очень нравится ответ Дэна Болье, но он не работает со 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()
}
}
}
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
// Conversion into base64 string
self.uploadImageString = uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
В 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);
}