ฉันจะ dispatch_sync, dispatch_async, dispatch_after และอื่น ๆ ใน Swift 3, Swift 4 และอื่น ๆ ได้อย่างไร


243

ฉันมีโค้ดจำนวนมากในโครงการ Swift 2.x (หรือ 1.x) ที่มีลักษณะดังนี้:

// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = image
    }
}

หรือสิ่งนี้เพื่อชะลอการดำเนินการ:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
    print("test")
}

หรือการใช้งานอื่น ๆ ทุกประเภทของ Grand Central Dispatch API ...

ตอนนี้ฉันได้เปิดโครงการของฉันใน Xcode 8 (เบต้า) สำหรับ Swift 3 ฉันได้รับข้อผิดพลาดทุกประเภท บางคนเสนอให้แก้ไขรหัสของฉัน แต่ไม่ใช่การแก้ไขทั้งหมดที่สร้างรหัสที่ใช้งานได้ ฉันจะทำอย่างไรกับสิ่งนี้


ตอบแล้วที่นี่: stackoverflow.com/questions/37805885/…
t1ser

คำตอบ:


343

ตั้งแต่เริ่มต้น Swift ได้จัดหาสิ่งอำนวยความสะดวกบางอย่างสำหรับการทำ ObjC และ C ให้ Swifty มากขึ้นเพิ่มมากขึ้นในแต่ละรุ่น ตอนนี้ใน Swift 3 คุณลักษณะ"นำเข้าในฐานะสมาชิก"ใหม่ช่วยให้เฟรมเวิร์กมีลักษณะบางอย่างของ C API - ซึ่งคุณมีชนิดข้อมูลที่ทำงานคล้ายคลาสและมีฟังก์ชันระดับโลกมากมายให้ใช้งานได้ - ทำหน้าที่เหมือน Swift-native APIs ชนิดข้อมูลที่นำเข้าเป็นคลาส Swift ฟังก์ชันส่วนกลางที่เกี่ยวข้องนำเข้าเป็นวิธีการและคุณสมบัติในคลาสเหล่านั้นและบางสิ่งที่เกี่ยวข้องเช่นชุดของค่าคงที่สามารถกลายเป็นชนิดย่อยตามความเหมาะสม

ใน Xcode 8 / Swift 3 beta, Apple ได้ใช้คุณสมบัตินี้ (รวมถึงรุ่นอื่น ๆ ) เพื่อทำให้เฟรมเวิร์ก Dispatch Swifty มีมากขึ้น (และกราฟิกคอร์เช่นกัน) หากคุณได้ติดตามความพยายามของโอเพ่นซอร์ส Swift นี่ไม่ใช่ข่าวแต่ตอนนี้เป็นครั้งแรกที่เป็นส่วนหนึ่งของ Xcode

ขั้นตอนแรกของคุณในการย้ายโครงการใด ๆ ไปยัง Swift 3ควรจะเปิดใน Xcode 8 และเลือกแก้ไข> แปลง> เป็นไวยากรณ์ Swift ปัจจุบัน ...ในเมนู สิ่งนี้จะนำไปใช้ (ด้วยการตรวจสอบและการอนุมัติของคุณ) การเปลี่ยนแปลงทั้งหมดในครั้งเดียวที่จำเป็นสำหรับ API ที่เปลี่ยนชื่อและการเปลี่ยนแปลงอื่น ๆ (บ่อยครั้งที่บรรทัดของรหัสได้รับผลกระทบจากการเปลี่ยนแปลงมากกว่าหนึ่งรายการในครั้งเดียวดังนั้นการตอบสนองต่อการแก้ไขข้อผิดพลาดอาจไม่สามารถจัดการทุกอย่างได้)

ผลลัพธ์ก็คือรูปแบบทั่วไปของการตีกลับงานเป็นพื้นหลังและกลับมาในลักษณะนี้:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

โปรดทราบว่าเราใช้.userInitiatedแทนDISPATCH_QUEUE_PRIORITYค่าคงที่แบบเก่าอย่างใดอย่างหนึ่ง ตัวระบุคุณภาพของการบริการ (QoS) ถูกนำมาใช้ใน OS X 10.10 / iOS 8.0 ซึ่งเป็นวิธีที่ชัดเจนสำหรับระบบในการจัดลำดับความสำคัญของงานและลดค่าตัวระบุลำดับความสำคัญเก่า ดูเอกสารประกอบของ Apple เกี่ยวกับการทำงานพื้นหลังและการประหยัดพลังงานเพื่อดูรายละเอียด

อย่างไรก็ตามถ้าคุณรักษาคิวของตัวเองเพื่อจัดระเบียบงานวิธีรับตอนนี้มีลักษณะดังนี้ (โปรดสังเกตว่านั่นDispatchQueueAttributesคือOptionSetเพื่อให้คุณใช้ตัวอักษรสไตล์คอลเลกชันเพื่อรวมตัวเลือก):

class Foo { 
    let queue = DispatchQueue(label: "com.example.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello World")
        }
    }
}

ใช้dispatch_afterเพื่อทำงานในภายหลังหรือไม่ นั่นเป็นวิธีการในคิวเช่นกันและใช้เวลาDispatchTimeซึ่งมีตัวดำเนินการสำหรับประเภทตัวเลขต่าง ๆ เพื่อให้คุณสามารถเพิ่มวินาทีหรือเศษส่วนได้ทั้งหมด:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}

คุณสามารถค้นหาเส้นทางของคุณใน Dispatch API ใหม่ได้โดยการเปิดอินเทอร์เฟซใน Xcode 8 - ใช้ Open อย่างรวดเร็วเพื่อค้นหาโมดูล Dispatch หรือใส่สัญลักษณ์ (เช่นDispatchQueue) ในโครงการ / สนามเด็กเล่น Swift ของคุณแล้วคลิกที่คำสั่ง โมดูลจากที่นั่น (คุณสามารถค้นหา Swift Dispatch API ได้ในเว็บไซต์อ้างอิง API ใหม่ของ Apple และโปรแกรมดูเอกสาร in-Xcode แต่ดูเหมือนว่าเนื้อหา doc จากเวอร์ชั่น C ยังไม่ได้ย้ายเข้ามาเลย)

ดูคู่มือการย้ายถิ่นสำหรับเคล็ดลับเพิ่มเติม


3
สำหรับ Xcode 8 Beta 6 นั้นคุณลักษณะ .serial จะหายไปและพฤติกรรมเริ่มต้น - Forums.developer.apple.com/message/159457#159457
hyouuu

6
ต้องมีการอัปเดตตั้งแต่ XCode 8.1 .. ป้ายกำกับแอตทริบิวต์ได้หายไปและเราสามารถใช้ 'DispatchQueue.global (qos: .background) .async' แทนได้ในตอนนี้
Mike M

2
คำตอบที่ยอดเยี่ยม จริงๆช่วยให้ฉันได้รับหัวของฉันมัน
Mohsin Khubaib Ahmed

ฉันต้องใช้qos:แทนattributes:
อิสลาม Q.

ไม่ว่าจะเป็นmyQueue.async {ในclass Fooตัวอย่าง?
vacawama

142

ใน Xcode 8 beta 4 ไม่ทำงาน ...

ใช้:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("Are we there yet?")
}

สำหรับ async สองวิธี:

DispatchQueue.main.async {
    print("Async1")
}

DispatchQueue.main.async( execute: {
    print("Async2")
})

ดังนั้นมันจึงไม่ปิดกั้น UI
25

72

อันนี้เป็นตัวอย่างที่ดีSwift 4เกี่ยวกับasync:

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}

hi DispatchQueue.main.async {// Run UI Updates} กำลังทำงานก่อนเธรดพื้นหลัง
Uma Achanta

คล้ายกับ coroutines ของ Kotlin
25


26

Swift 5.2, 4 และใหม่กว่า

คิวหลักและพื้นหลัง

let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread") 

ทำงานกับ async และหัวข้อการซิงค์ !

 background.async { //async tasks here } 
 background.sync { //sync tasks here } 

เธรด Async จะทำงานพร้อมกับเธรดหลัก

เธรดการซิงค์จะบล็อกเธรดหลักขณะดำเนินการ


1
และวิธีการที่คุณจะใช้ซิงค์กระทู้โดยไม่ปิดกั้นหัวข้อหลัก (UI) ฉันต้องการที่จะดำเนินการแถวของสิ่งต่าง ๆ ในพื้นหลัง - แต่สิ่งเหล่านี้จะต้องดำเนินการอย่างใดอย่างหนึ่งหลังจากที่อื่น ๆ ในทางซิงค์ ในช่วงเวลานี้ UI ควรตอบสนองต่อไป .... คุณจะทำอย่างไร
iKK

ใช้ NSOperationQueue ซึ่งแต่ละงานของคุณเป็นตัวแทนของ NSOperation ดูstackoverflow.com/a/19746890/5215474
Saranjith

12

Swift 4.1 และ 5เราใช้งานคิวในหลาย ๆ ที่ในรหัสของเรา ดังนั้นฉันจึงสร้างคลาสเธรดด้วยคิวทั้งหมด หากคุณไม่ต้องการใช้คลาส Threads คุณสามารถคัดลอกรหัสคิวที่ต้องการจากวิธีการเรียน

class Threads {

  static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
  static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")

  // Main Queue
  class func performTaskInMainQueue(task: @escaping ()->()) {
    DispatchQueue.main.async {
      task()
    }
  }

  // Background Queue
  class func performTaskInBackground(task:@escaping () throws -> ()) {
    DispatchQueue.global(qos: .background).async {
      do {
        try task()
      } catch let error as NSError {
        print("error in background thread:\(error.localizedDescription)")
      }
    }
  }

  // Concurrent Queue
  class func perfromTaskInConcurrentQueue(task:@escaping () throws -> ()) {
    concurrentQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Concurrent Queue:\(error.localizedDescription)")
      }
    }
  }

  // Serial Queue
  class func perfromTaskInSerialQueue(task:@escaping () throws -> ()) {
    serialQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Serial Queue:\(error.localizedDescription)")
      }
    }
  }

  // Perform task afterDelay
  class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:@escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
      task()
    }
  }
}

ตัวอย่างการใช้คิวหลัก

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.