Como usar o thread de fundo no swift?

329

Como usar o threading no swift?

dispatchOnMainThread:^{

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

}];
Anshul
fonte
Qual parte você tem problemas para converter?
Nschum
2
Por que você tem ]antes do ponto e vírgula na última linha?
akashivskyy
3
seria útil se você explicasse onde está preso ou com o qual precisa de ajuda.
Nsuinteger
4
Você deve aceitar a resposta correta, se realmente o ajudar, também ajudará outros a encontrar a solução correta.
Amit Singh
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." } }
Anurag Sharma

Respostas:

708

Swift 3.0+

Muito foi modernizado no Swift 3.0. A execução de algo no segmento de plano de fundo se parece com o seguinte:

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 a 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 - Problema conhecido

A partir do Swift 1.1, a Apple não suportava a sintaxe acima sem algumas modificações. Passar QOS_CLASS_BACKGROUNDrealmente não funcionou; em vez disso, use Int(QOS_CLASS_BACKGROUND.value).

Para obter mais informações, consulte a documentação da Apple.

tobiasdm
fonte
23
E se alguém quiser um mais Swift como a sintaxe, criei Async que adiciona um pouco de açúcar para a sintaxe comoAsync.background {}
tobiasdm
Eu estou usando seu código no xCode 6.0.1 e no ios 8.Ele fornece erro como classe de retorno "QOS_CLASS_BACKGROUND" e é do tipo UInt32 e "dispatch_get_global_queue" requer o 1º parâmetro como int, portanto, o erro de tipo está chegando.
Zalak Patel
Portanto, no Xcode 6.1.1, não estou recebendo um erro ao usar "QOS_CLASS_BACKGROUND". Está consertado?
Lucas Goossen
@LucasGoossen Sim, foi corrigido. Atualizei a postagem de acordo.
Tobiasdm
1
@NikitaPronchik Isso não está claro na resposta? Caso contrário, fique à vontade para fazer uma edição.
Tobiasdm 03/03
123

A melhor prática é definir uma função reutilizável que possa ser acessada várias vezes.

FUNÇÃO REUTILIZÁVEL:

por exemplo, em algum lugar como AppDelegate.swift como uma função global.

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

Nota: em Swift 2.0, substituir QOS_CLASS_USER_INITIATED.value acima com QOS_CLASS_USER_INITIATED.rawValue vez

USO:

A. Para executar um processo em segundo plano com um atraso de 3 segundos:

    backgroundThread(3.0, background: {
            // Your background function here
    })

B. Para executar um processo em segundo plano, execute uma conclusão em primeiro plano:

    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. Atraso em 3 segundos - observe o uso do parâmetro de conclusão sem o parâmetro de segundo plano:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })
Dale Clifford
fonte
1
bom trecho, deve ser a resposta correta. @Dale Clifford
LoVo
Abordagem Swift-y moderna de alto nível e brilhante para acessar métodos GCD antigos da biblioteca C de baixo nível. Deve vir padrão no Swift.
Craig Grummitt 01/09/2015
2
Muito agradável. Você poderia confirmar, o atraso só funciona para o bloco de conclusão. Isso significa que o atraso em A. não tem impacto e o bloco em segundo plano é executado imediatamente sem demora.
ObjectiveTC
1
Você deve ser capaz de substituir if(background != nil){ background!(); }com background?()uma sintaxe um pouco swiftier?
Simon Bengtsson
1
Você poderia atualizar isso para o Swift 3? O conversor automático transformou-o, DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {mas isso gera um erro como cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Uma solução de trabalho é encontrada aqui ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL
111

A resposta de Dan Beaulieu no swift5 (também trabalhando desde o 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()
                })
            }
        }
    }

}

Uso

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
fonte
Incrível, obrigado por atualizar tão bem o formato Swift 3.0.1!
Dale Clifford
1
Eu uso extensões mais do que qualquer pessoa viva. Mas há um risco real de usar uma extensão que não é diferente da original!
Fattie
@Frouo Muito elegante, é possível adicionar um manipulador de conclusão quando 4 assinaturas assíncronas terminam? Eu sei que é um pouco fora de tópico.
eonist
1
Sim, esqueça esse link. tudo o que você precisa é de um grupo de expedição - é muito, muito simples; não se preocupe!
Fattie
1
@DilipJangid você não pode, a menos que seu trabalho no backgroundencerramento seja muito, muito, muito longo (~ = infinito). Esse método é feito para durar um tempo finito: o tempo que seu trabalho em segundo plano precisa executar. Portanto, o completionfechamento será chamado assim que o tempo de execução do trabalho em segundo plano + o atraso passar.
Frouo 30/05
42

Versão Swift 3

O Swift 3 utiliza nova DispatchQueueclasse para gerenciar filas e threads. Para executar algo no thread de segundo plano, você usaria:

let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async {
    print("Run on background thread")
}

Ou se você quiser algo em duas linhas de código:

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

Você também pode obter informações detalhadas sobre o GDC no Swift 3 neste tutorial .

Disse Sikira
fonte
Disse. Como a sua resposta é a melhor, introduzi uma linha de código mostrando como você "liga de volta quando termina". Sinta-se livre para relaxar ou editar, aplausos
Fattie
35

Do tutorial de Jameson Quave

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
alex
fonte
3
Apenas para esclarecimento, por que isso seria usado em vez da resposta aceita? Esta é apenas uma API mais antiga?
Sirenes
1
@Sirens Eu acho que isso seria muito útil para aplicações de suporte <iOS 8.
bperdue
Eu uso isso no iOs 8.2 para forçar processos.
μολὼν.λαβέ
DISPATCH_QUEUE_PRIORITY_DEFAULT reverte para QOS_CLASS_DEFAULT. Então, acho que você poderia dizer que é uma sintaxe de alto nível / aceita.
PostCodeism
34

No Swift 4.2 e no Xcode 10.1

Temos três tipos de filas:

1. Fila principal: a fila principal é uma fila serial criada pelo sistema e associada ao encadeamento principal do aplicativo.

2. Fila Global: A fila global é uma fila simultânea que podemos solicitar com relação à prioridade das tarefas.

3. Filas personalizadas: podem ser criadas pelo usuário. As filas simultâneas personalizadas sempre são mapeadas para uma das filas globais, especificando uma propriedade de Qualidade de serviço (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

Todas essas filas podem ser executadas de duas maneiras

1. Execução síncrona

2. Execução assíncrona

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

No 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
fonte
1
Melhor tutorial para threads medium.com/@gabriel_lewis/…
iOS
Eu não vi nenhuma alteração quando você usa .background QoS ou .userInitiatedmas para mim funcionou com.background
ferrugem
24

Swift 4.x

Coloque isso em algum arquivo:

func background(work: @escaping () -> ()) {
    DispatchQueue.global(qos: .userInitiated).async {
        work()
    }
}

func main(work: @escaping () -> ()) {
    DispatchQueue.main.async {
        work()
    }
}

e depois chame onde precisar:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}
Hadži Lazar Pešić
fonte
22

Você precisa separar as alterações que deseja executar em segundo plano das atualizações que deseja executar na interface do usuário:

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

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}
phuongle
fonte
dispatch_async(dispatch_get_main_queue()) { // update some UI }É assim chamado quando a instrução background (Outer Block) é executada?
fácil
Isso não é apenas para o Swift 2.3 e abaixo?
Surz 23/09/16
9

Boas respostas, no entanto, de qualquer maneira, quero compartilhar minha solução Orientada a Objetos Atualizada para o Swift 5 .

confira: AsyncTask

Inspirado conceitualmente pelo AsyncTask do Android, escrevi minha própria aula no Swift

O AsyncTask permite o uso adequado e fácil do thread da interface do usuário. Essa classe permite executar operações em segundo plano e publicar resultados no thread da interface do usuário.

Aqui estão alguns exemplos de uso

Exemplo 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'

Exemplo 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

Possui 2 tipos genéricos:

  • BGParam - o tipo do parâmetro enviado para a tarefa após a execução.
  • BGResult - o tipo do resultado da computação em segundo plano.

    Ao criar um AsyncTask, você pode esses tipos para o que for necessário para passar e sair da tarefa em segundo plano, mas se você não precisar desses tipos, poderá marcá-lo como não utilizado, apenas configurando-o para: Voidou com sintaxe mais curta:()

Quando uma tarefa assíncrona é executada, ela realiza três etapas:

  1. beforeTask:()->Void chamado no thread da interface do usuário imediatamente antes da tarefa ser executada.
  2. backgroundTask: (param:BGParam)->BGResult chamado no encadeamento em segundo plano imediatamente após
  3. afterTask:(param:BGResult)->Void chamado no encadeamento da interface do usuário com resultado da tarefa em segundo plano
Nikita Kurtin
fonte
4
Isso funciona maravilhosamente para mim. Bom trabalho, por que não colocá-lo no github?
36
8

Como a pergunta do OP já foi respondida acima, só quero adicionar algumas considerações de velocidade:

Não recomendo executar tarefas com a prioridade do encadeamento .background, especialmente no iPhone X, onde a tarefa parece estar alocada nos núcleos de baixa energia.

Aqui estão alguns dados reais de uma função computacionalmente intensiva que lê um arquivo XML (com buffer) e executa a interpolação de dados:

Nome do dispositivo / .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

Observe que o conjunto de dados não é o mesmo para todos os dispositivos. É o maior do iPhone X e o menor do iPhone 5s.

Cosmin
fonte
4

Swift 5

Para facilitar, crie um arquivo "DispatchQueue + Extensions.swift" com este conteúdo:

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

Uso:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}
Xys
fonte
2

O Grand Central Dispatch é usado para lidar com multitarefa em nossos aplicativos para iOS.

Você pode usar este código

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

Mais informações usam este link: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html

Anil Dhameliya
fonte
2

Função multiuso para rosca

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

Use-o como:

performOn(.Background) {
    //Code
}
Viral Savaliya
fonte
1

Gosto muito da resposta de Dan Beaulieu, mas ela não funciona com o Swift 2.2 e acho que podemos evitar aqueles desagradáveis ​​arranjos forçados!

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

no Swift 4.2, isso funciona.

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
fonte