Làm thế nào để sử dụng chủ đề nền trong swift?


328

Làm thế nào để sử dụng luồng trong swift?

dispatchOnMainThread:^{

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

}];

Phần nào bạn gặp khó khăn khi chuyển đổi?
nschum

2
Tại sao bạn có ]trước dấu chấm phẩy ở dòng cuối cùng?
akashivskyy

3
Sẽ rất hữu ích nếu bạn giải thích nơi bạn bị mắc kẹt hoặc bạn cần giúp đỡ.
nsuinteger

4
Bạn phải chấp nhận câu trả lời đúng nếu nó thực sự giúp bạn, nó cũng sẽ giúp người khác tìm ra giải pháp chính xác.
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

Câu trả lời:


707

Swift 3.0+

Rất nhiều thứ đã được hiện đại hóa trong Swift 3.0. Chạy một cái gì đó trên chủ đề nền trông như thế này:

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 đến 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 - Vấn đề đã biết

Kể từ Swift 1.1, Apple đã không hỗ trợ cú pháp trên mà không có một số sửa đổi. Vượt qua QOS_CLASS_BACKGROUNDkhông thực sự làm việc, thay vào đó sử dụng Int(QOS_CLASS_BACKGROUND.value).

Để biết thêm thông tin, xem tài liệu Táo


23
Và nếu ai đó muốn một cú pháp giống Swift hơn, tôi đã tạo Async thêm một số đường vào cú pháp nhưAsync.background {}
tobiasdm

Tôi đang sử dụng mã của bạn trong xCode 6.0.1 và ios 8. Nó báo lỗi là lớp trả về "QOS_CLASS_BACKGROUND" và nó thuộc loại UInt32 và "Clark_get_global_queue" yêu cầu tham số thứ nhất vì lỗi int đang đến.
Zalak Patel

Vì vậy, trong Xcode 6.1.1 tôi không gặp phải lỗi khi chỉ sử dụng "QOS_CLASS_BACKGROUND" đơn giản. Nó đã được khắc phục?
Lucas Goossen

@LucasGoossen Vâng, nó đã được sửa. Tôi đã cập nhật bài viết cho phù hợp.
tobiasdm

1
@NikitaPronchik Không rõ câu trả lời này sao? Khác cảm thấy tự do để thực hiện một chỉnh sửa cho nó.
tobiasdm

122

Thực tiễn tốt nhất là xác định một chức năng có thể sử dụng lại có thể được truy cập nhiều lần.

CHỨC NĂNG CẦN THIẾT:

ví dụ như một nơi nào đó như AppDelegate.swift là Chức năng toàn cầu.

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

Lưu ý: trong Swift 2.0, thay thế QOS_CLASS_USER_INITIATED.value ở trên bằng QOS_CLASS_USER_INITIATED.rawValue thay thế

SỬ DỤNG:

A. Để chạy một quá trình trong nền với độ trễ 3 giây:

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

B. Để chạy một quá trình trong nền sau đó chạy hoàn thành ở nền trước:

    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. Để trì hoãn 3 giây - lưu ý sử dụng tham số hoàn thành không có tham số nền:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })

1
đoạn trích hay, nên là câu trả lời đúng. @Dale Clifford
LoVo

Phương pháp tiếp cận Swift-y hiện đại cấp cao rực rỡ để truy cập các phương pháp GCD lâu đời từ thư viện C cấp thấp. Nên đến tiêu chuẩn trong Swift.
Craig Grummitt

2
Rất đẹp. Bạn vui lòng xác nhận, độ trễ chỉ hoạt động cho khối hoàn thành. Vì vậy, điều đó có nghĩa là độ trễ trong A. không có tác động và khối nền thực thi ngay lập tức mà không bị trễ.
ObjectiveTC

1
Bạn có thể thay thế if(background != nil){ background!(); }bằng background?()một cú pháp nhanh hơn?
Simon Bengtsson

1
Bạn có thể vui lòng cập nhật cái này cho Swift 3 không? Bộ chuyển đổi tự động biến nó thành DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {nhưng điều này gây ra lỗi như thế nào cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Một giải pháp làm việc được tìm thấy ở đây ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL

110

Câu trả lời của Dan Beaulieu trong swift5 (cũng hoạt động kể từ 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()
                })
            }
        }
    }

}

Sử dụng

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

Thật tuyệt vời, cảm ơn bạn đã cập nhật độc đáo cho định dạng Swift 3.0.1!
Dale Clifford

1
Tôi sử dụng phần mở rộng nhiều hơn bất kỳ người sống. Nhưng có một mối nguy hiểm thực sự trong việc sử dụng một tiện ích mở rộng không khác gì so với bản gốc!
Fattie

@Frouo Rất thanh lịch, có thể thêm một trình xử lý hoàn thành để khi 4 async gọi tất cả kết thúc không? Tôi biết nó hơi lạc đề.
eonist

1
yup quên liên kết đó. tất cả những gì bạn cần là một nhóm điều phối - nó rất đơn giản; không có lo lắng gì cả !
Fattie

1
@DilipJangid bạn không thể, trừ khi công việc của bạn backgroundđóng cửa rất rất rất dài (~ = vô hạn). Phương pháp này được thực hiện để tồn tại trong một thời gian hữu hạn: thời gian công việc nền của bạn cần thực hiện. Vì vậy, việc completionđóng cửa sẽ được gọi ngay khi thời gian thực hiện công việc nền + thời gian trễ của bạn trôi qua.
frouo

42

Phiên bản Swift 3

Swift 3 sử dụng DispatchQueuelớp mới để quản lý hàng đợi và chuỗi. Để chạy một cái gì đó trên luồng nền, bạn sẽ sử dụng:

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

Hoặc nếu bạn muốn một cái gì đó trong hai dòng mã:

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

Bạn cũng có thể nhận được một số thông tin chuyên sâu về GDC trong Swift 3 trong hướng dẫn này .


Nói. Vì câu trả lời của bạn là tốt nhất, tôi đã ném vào một dòng mã cho biết cách bạn "gọi lại khi kết thúc". Hãy thư giãn hoặc chỉnh sửa, chúc mừng
Fattie

35

Từ hướng dẫn của Jameson Quave

Swift 2

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

3
Chỉ cần làm rõ, tại sao điều này sẽ được sử dụng thay vì câu trả lời được chấp nhận? Đây có phải chỉ là một API cũ?
Còi báo

1
@Sirens Tôi nghĩ rằng điều này sẽ rất hữu ích cho các ứng dụng hỗ trợ <iOS 8.
bperdue

Tôi sử dụng điều này cho iOs 8.2 để buộc các quy trình.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT hoàn nguyên về QOS_CLASS_DEFAULT. Vì vậy, tôi đoán bạn có thể nói đó là cú pháp được chấp nhận ở cấp độ cao hơn.
PostCodeism

34

Trong Swift 4.2 và Xcode 10.1

Chúng tôi có ba loại Hàng đợi:

1. Hàng đợi chính: Hàng đợi chính là hàng đợi nối tiếp được tạo bởi hệ thống và được liên kết với luồng chính của ứng dụng.

2. Hàng đợi toàn cầu: Hàng đợi toàn cầu là một hàng đợi đồng thời mà chúng ta có thể yêu cầu đối với mức độ ưu tiên của các nhiệm vụ.

3. Hàng đợi tùy chỉnh: có thể được tạo bởi người dùng. Hàng đợi đồng thời tùy chỉnh luôn được ánh xạ vào một trong các hàng đợi toàn cầu bằng cách chỉ định thuộc tính Chất lượng dịch vụ (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

Tất cả các hàng đợi này có thể được thực thi theo hai cách

1. Thực hiện đồng bộ

2. Thực thi không đồng bộ

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

Từ 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
Hướng dẫn tốt nhất cho chủ đề Medium.com/@gabriel_lewis/ Kẻ
iOS

Tôi không thấy bất kỳ thay đổi nào khi bạn sử dụng .background QoS hoặc .userInitiatedđối với tôi, nó đã hoạt động với.background
rỉ sét

24

Swift 4.x

Đặt cái này trong một số tập tin:

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

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

và sau đó gọi nó là nơi bạn cần:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}

22

Bạn phải tách biệt các thay đổi mà bạn muốn chạy trong nền khỏi các bản cập nhật bạn muốn chạy trên UI:

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

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}

Vì vậy, dispatch_async(dispatch_get_main_queue()) { // update some UI }được gọi khi câu lệnh nền (Khối ngoài) được thực thi?
justColbs 7/11/2015

Đây không phải chỉ dành cho Swift 2.3 trở xuống sao?
Surz

9

Mặc dù vậy, câu trả lời tốt, dù sao tôi cũng muốn chia sẻ giải pháp Hướng đối tượng của mình Cập nhật cho swift 5 .

vui lòng kiểm tra xem: AsyncTask

Lấy cảm hứng từ khái niệm AsyncTask của Android, tôi đã viết lớp của riêng mình trong Swift

AsyncTask cho phép sử dụng luồng UI thích hợp và dễ dàng. Lớp này cho phép thực hiện các hoạt động nền và xuất bản kết quả trên luồng UI.

Dưới đây là một vài ví dụ sử dụng

Ví dụ 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'

Ví dụ 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

Nó có 2 loại chung:

  • BGParam - loại tham số được gửi đến tác vụ khi thực hiện.
  • BGResult - loại kết quả của tính toán nền.

    Khi bạn tạo AsyncTask, bạn có thể loại các loại đó thành bất cứ thứ gì bạn cần để chuyển vào và ra khỏi tác vụ nền, nhưng nếu bạn không cần các loại đó, bạn có thể đánh dấu nó là không sử dụng chỉ bằng cách đặt nó thành: Voidhoặc với cú pháp ngắn hơn:()

Khi một tác vụ không đồng bộ được thực thi, nó sẽ trải qua 3 bước:

  1. beforeTask:()->Void được gọi trên luồng UI ngay trước khi tác vụ được thực thi.
  2. backgroundTask: (param:BGParam)->BGResult được gọi trên luồng nền ngay sau đó
  3. afterTask:(param:BGResult)->Void được gọi trên luồng UI với kết quả từ tác vụ nền

4
Điều này làm việc tuyệt vời cho tôi. Làm tốt lắm, tại sao không đặt nó trên github?
36 theo thiết kế

8

Vì câu hỏi OP đã được trả lời ở trên, tôi chỉ muốn thêm một số cân nhắc về tốc độ:

Tôi không khuyên bạn nên chạy các tác vụ với mức độ ưu tiên của luồng .background, đặc biệt là trên iPhone X, nơi nhiệm vụ dường như được phân bổ trên các lõi năng lượng thấp.

Đây là một số dữ liệu thực từ một hàm chuyên sâu tính toán đọc từ tệp XML (có bộ đệm) và thực hiện phép nội suy dữ liệu:

Tên thiết bị / .background / .utility / .DEFAULT / .userInitiated / .userInteractive

  1. iPhone X: 18,7 giây / 6,3 giây / 1,8 giây / 1,8 giây / 1,8 giây
  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

Lưu ý rằng tập dữ liệu không giống nhau cho tất cả các thiết bị. Đó là lớn nhất trên iPhone X và nhỏ nhất trên iPhone 5s.


4

Swift 5

Để làm cho nó dễ dàng, hãy tạo một tệp "DispatchQueue + Extended.swift" với nội dung này:

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

Sử dụng :

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch được sử dụng để xử lý đa nhiệm trong các ứng dụng iOS của chúng tôi.

Bạn có thể sử dụng mã này

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

Thông tin khác sử dụng liên kết này: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

Chức năng đa mục đích cho chủ đề

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

Sử dụng nó như:

performOn(.Background) {
    //Code
}

1

Tôi thực sự thích câu trả lời của Dan Beaulieu, nhưng nó không hoạt động với Swift 2.2 và tôi nghĩ chúng ta có thể tránh được những cuộc cưỡng bức khó chịu đó!

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

-3

trong Swift 4.2 này hoạt động.

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.