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

240

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

dispatchOnMainThread:^{

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

}];
  • 0
    Какую часть у вас есть проблемы с преобразованием?
  • 1
    Почему у вас есть ] перед точкой с запятой в последней строке?
Показать ещё 2 комментария
Теги:

14 ответов

578

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

Swift 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).

Для получения дополнительной информации см. Документация яблок

  • 23
    И если кто-то хочет более похожий на Swift синтаксис, я создал Async, который добавляет немного сахара в синтаксис, такой как Async.background {}
  • 0
    Я использую ваш код в xCode 6.0.1 и ios 8. Он выдает ошибку как класс возврата «QOS_CLASS_BACKGROUND» и имеет тип UInt32, а для «dispatch_get_global_queue» требуется 1-й параметр, так как тип int приводит к ошибке типа.
Показать ещё 6 комментариев
118

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

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

например, где-то вроде 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
    хороший фрагмент, должен быть правильный ответ. @ Дейл Клиффорд
  • 0
    Великолепный современный высокоуровневый подход Swift-y для доступа к устаревшим методам GCD из низкоуровневой библиотеки C. Должно прийти стандартно в Swift.
Показать ещё 8 комментариев
43

Дэн Боулье отвечает в 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
})
  • 0
    Удивительно, спасибо, что так приятно обновили до формата Swift 3.0.1!
  • 1
    Я использую расширения больше, чем любой живой человек. Но есть реальная опасность в использовании расширения, которое ничем не отличается от оригинала!
Показать ещё 7 комментариев
39

версия 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 в этом учебнике.

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

Из учебник Jameson Quave

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
  • 3
    Просто для пояснения, почему это будет использоваться вместо принятого ответа? Это просто старый API?
  • 1
    @ Сирены Я думаю, это было бы очень полезно для приложений, поддерживающих <iOS 8.
Показать ещё 3 комментария
23

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

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

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

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

Хорошие ответы, хотя, в любом случае, я хочу поделиться своим объектно-ориентированным решением Обновлено для быстрого 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 или с более коротким синтаксисом: ()

Когда выполняется асинхронная задача, она проходит три этапа:

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

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

Я не рекомендую запускать задачи с приоритетом.background thread, особенно на iPhone X, где задача, по-видимому, выделяется на ядрах с малой мощностью.

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

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

  1. iPhone X: 18.7s/6.3s/1.8s/1.8s/1.8s
  2. iPhone 7: 4.6s/3.1s/3.0s/2.8s/2.6s
  3. iPhone 5s: 7.3s/6.1s/4.0s/4.0s/3.8s

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

1

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

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

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()
            }
        }
    }
}
0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
-4

в 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);
}
  • 0
    не показывать while(true) в таком примере ..

Ещё вопросы

Сообщество Overcoder
Наверх
Меню