Как использовать фоновый поток в Swift?

329

Как использовать потоки в Swift?

dispatchOnMainThread:^{

    NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));

}];
Anshul
источник
Какую часть у вас есть проблемы с преобразованием?
nschum
2
Почему у вас ]перед точкой с запятой в последней строке?
Акашивский
3
было бы полезно, если бы вы объяснили, где вы застряли или с чем вам нужна помощь.
nsuinteger
4
Вы должны принять правильный ответ, если он действительно поможет вам, он поможет другим также найти правильное решение.
Амит Сингх
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." } }
Анураг Шарма

Ответы:

708

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.

tobiasdm
источник
23
И если кто-то хочет более похожий на Swift синтаксис, я создал Async, который добавляет немного сахара к синтаксису вродеAsync.background {}
tobiasdm
Я использую ваш код в xCode 6.0.1 и ios 8. Он выдает ошибку как класс возврата «QOS_CLASS_BACKGROUND» и имеет тип UInt32, а «dispatch_get_global_queue» требует 1-го параметра как int, так что возникает ошибка типа.
Залак Патель
Таким образом, в Xcode 6.1.1 я не получаю ошибку за использование просто простого "QOS_CLASS_BACKGROUND". Это исправлено?
Лукас Гуссен
@ LucasGoossen Да, это было исправлено. Я обновил пост соответственно.
Тобиасдм
1
@NikitaPronchik Разве это не ясно из ответа? Еще не стесняйтесь вносить изменения в него.
Тобиасдм
123

Рекомендуется определить функцию многократного использования, к которой можно обращаться несколько раз.

ВОЗМОЖНАЯ ФУНКЦИЯ:

например, где-то вроде 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
    })

Б. Чтобы запустить процесс в фоновом режиме, затем выполнить завершение на переднем плане:

    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
    })
Дейл Клиффорд
источник
1
хороший фрагмент, должен быть правильный ответ. @ Дейл Клиффорд
LoVo
Великолепный современный высокоуровневый подход Swift-y для доступа к старым GCD-методам из низкоуровневой библиотеки C. Должно прийти стандартно в Swift.
Крейг Груммитт
2
Очень хорошо. Подтвердите, задержка работает только для блока завершения. Таким образом, это означает, что задержка в A. не оказывает влияния, и фоновый блок выполняется немедленно без задержки.
ObjectiveTC
1
Вы должны быть в состоянии заменить if(background != nil){ background!(); }с background?()на несколько swiftier синтаксис?
Саймон Бенгтссон
1
Не могли бы вы обновить это для 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)'. Рабочее решение находится здесь ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-IL
111

Ответ Дана Болье в swift5 (также работает с swift 3.0.1).

Swift 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
})
frouo
источник
Удивительно, спасибо, что так приятно обновили до формата Swift 3.0.1!
Дейл Клиффорд
1
Я использую расширения больше, чем любой живой человек. Но есть реальная опасность в использовании расширения, которое ничем не отличается от оригинала!
Толстяк
@Frouo Очень элегантно, можно ли добавить обработчик завершения, когда все 4 асинхронных вызова заканчиваются? Я знаю, это немного не по теме.
eonist
1
да забудь эту ссылку. все, что вам нужно, это группа рассылки - это очень очень просто; не беспокойтесь вообще!
Толстяк
1
@DilipJangid вы не можете, если ваша работа в backgroundзакрытии очень, очень, очень долго (~ = бесконечно). Этот метод рассчитан на конечное время: время, необходимое для выполнения фоновой работы. Таким образом, completionзамыкание будет вызвано, как только пройдет время выполнения фоновой работы + задержка.
frouo
42

Версия 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 .

Саид Сикира
источник
Сказал. Поскольку ваш ответ лучший, я добавил строку кода, показывающую, как вы «перезвоните, когда закончите». Не стесняйтесь раскручивать или редактировать, ура
Толстяк
35

От Jameson Quave в руководстве

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
Алекс
источник
3
Просто для пояснения, почему это будет использоваться вместо принятого ответа? Это просто старый API?
Сирены
1
@ Сирены. Я думаю, это было бы очень полезно для приложений, поддерживающих <iOS 8.
bperdue
Я использую это для IOS 8.2, чтобы форсировать процессы.
μολὼν.λαβέ
DISPATCH_QUEUE_PRIORITY_DEFAULT возвращается к QOS_CLASS_DEFAULT. Так что, я думаю, вы могли бы сказать, что это более высокий уровень / принятый синтаксис.
PostCodeism
34

В Swift 4.2 и Xcode 10.1

У нас есть три типа очередей:

1. Основная очередь: Главная очередь - это последовательная очередь, которая создается системой и связана с основным потоком приложения.

2. Глобальная очередь: Глобальная очередь - это параллельная очередь, которую мы можем запросить относительно приоритета задач.

3. Пользовательские очереди: могут быть созданы пользователем. Настраиваемые параллельные очереди всегда отображаются в одну из глобальных очередей путем указания свойства Quality of Service (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)
    }
}
IOS
источник
1
Лучший учебник для резьба medium.com/@gabriel_lewis/...
IOS
Я не видел никаких изменений, когда вы используете .background QoS, .userInitiatedно у меня все получилось.background
ржавчина
24

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)
     }
}
Хаджи Лазар Пешич
источник
22

Вы должны отделить изменения, которые вы хотите запустить в фоновом режиме, от обновлений, которые вы хотите запустить в пользовательском интерфейсе:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do your task

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}
phuongle
источник
Так dispatch_async(dispatch_get_main_queue()) { // update some UI }вызывается, когда выполняется фоновый оператор (Outer Block)?
justColbs
Разве это не только для Swift 2.3 и ниже?
Surz
9

Хорошие ответы , хотя, в любом случае я хочу поделиться моим объектно - ориентированным решением вплоть до настоящего времени для стрижа 5 .

пожалуйста, проверьте это: 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 этапа:

  1. beforeTask:()->Void вызывается в потоке пользовательского интерфейса непосредственно перед выполнением задачи.
  2. backgroundTask: (param:BGParam)->BGResult вызывается в фоновом потоке сразу после
  3. afterTask:(param:BGResult)->Void вызывается в потоке пользовательского интерфейса с результатом из фоновой задачи
Никита Куртин
источник
4
Это прекрасно работает для меня. Хорошая работа, почему бы не поставить его на github?
36 По дизайну
8

Поскольку на вопрос ОП уже был дан ответ, я просто хочу добавить некоторые соображения по поводу скорости:

Я не рекомендую запускать задачи с приоритетом потока .background, особенно на iPhone X, где задача, кажется, распределена на ядрах с низким энергопотреблением.

Вот некоторые реальные данные из вычислительно интенсивной функции, которая читает из файла XML (с буферизацией) и выполняет интерполяцию данных:

Имя устройства / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18,7 с / 6,3 с / 1,8 с / 1,8 с / 1,8 с
  2. iPhone 7: 4,6 с / 3,1 с / 3,0 с / 2,8 с / 2,6 с
  3. iPhone 5s: 7,3 с / 6,1 с / 4,0 с / 4,0 с / 3,8 с

Обратите внимание, что набор данных не одинаков для всех устройств. Это самый большой на iPhone X и самый маленький на iPhone 5s.

Космин
источник
4

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
    }
}
XYS
источник
2

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

Анил Дхамелия
источник
2

Многоцелевая функция для потока

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
}
Вирусная Савалия
источник
1

Мне очень нравится ответ Дэна Болье, но он не работает со 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()
            }
        }
    }
}
rougeExciter
источник
0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
AG
источник
-3

в 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);
}
Ackers
источник