ตามที่ระบุไว้ที่นี่และในคำตอบสำหรับคำถาม SO อื่น ๆ คุณไม่ต้องการใช้beginBackgroundTask
เฉพาะเมื่อแอปของคุณจะเข้าสู่พื้นหลัง ในทางตรงกันข้ามคุณควรใช้งานพื้นหลังสำหรับการใด ๆการดำเนินการใช้เวลานานซึ่งเสร็จสิ้นคุณต้องการเพื่อให้แน่ใจว่าแม้ว่าแอปไม่ไปเป็นพื้นหลัง
ดังนั้นรหัสของคุณน่าจะจบลงด้วยการซ้ำซ้อนของรหัสต้นแบบเดียวกันสำหรับการโทรbeginBackgroundTask
และendBackgroundTask
สอดคล้องกัน เพื่อป้องกันการเกิดซ้ำนี้เป็นเรื่องสมควรอย่างยิ่งที่จะต้องจัดแพคเกจสำเร็จรูปเป็นเอนทิตีห่อหุ้มเดี่ยว
ฉันชอบคำตอบที่มีอยู่ในการทำเช่นนั้น แต่ฉันคิดว่าวิธีที่ดีที่สุดคือการใช้คลาสย่อยของ Operation:
คุณสามารถจัดคิวการดำเนินการบน OperationQueue ใด ๆ และจัดการคิวนั้นได้ตามที่เห็นสมควร ตัวอย่างเช่นคุณมีอิสระที่จะยกเลิกการดำเนินการใด ๆ ที่มีอยู่ในคิวก่อนเวลาอันควร
หากคุณมีสิ่งที่ต้องทำมากกว่าหนึ่งอย่างคุณสามารถเชื่อมโยงการดำเนินงานเบื้องหลังหลาย ๆ งานได้ การพึ่งพาการสนับสนุนการดำเนินงาน
คิวการดำเนินการสามารถ (และควร) เป็นคิวพื้นหลัง ดังนั้นจึงไม่จำเป็นต้องกังวลเกี่ยวกับการดำเนินการโค้ดแบบอะซิงโครนัสภายในงานของคุณเนื่องจากการดำเนินการเป็นรหัสอะซิงโครนัส (อันที่จริงมันไม่มีเหตุผลที่จะเรียกใช้รหัสอะซิงโครนัสอีกระดับหนึ่งภายในการดำเนินการเนื่องจากการดำเนินการจะเสร็จสิ้นก่อนที่รหัสนั้นจะเริ่มทำงานหากคุณต้องการทำเช่นนั้นคุณจะต้องใช้การดำเนินการอื่น)
นี่คือคลาสย่อยของ Operation ที่เป็นไปได้:
class BackgroundTaskOperation: Operation {
var whatToDo : (() -> ())?
var cleanup : (() -> ())?
override func main() {
guard !self.isCancelled else { return }
guard let whatToDo = self.whatToDo else { return }
var bti : UIBackgroundTaskIdentifier = .invalid
bti = UIApplication.shared.beginBackgroundTask {
self.cleanup?()
self.cancel()
UIApplication.shared.endBackgroundTask(bti) // cancellation
}
guard bti != .invalid else { return }
whatToDo()
guard !self.isCancelled else { return }
UIApplication.shared.endBackgroundTask(bti) // completion
}
}
ควรจะเห็นได้ชัดว่าจะใช้สิ่งนี้อย่างไร แต่ในกรณีที่ไม่เป็นเช่นนั้นให้จินตนาการว่าเรามี OperationQueue ทั่วโลก:
let backgroundTaskQueue : OperationQueue = {
let q = OperationQueue()
q.maxConcurrentOperationCount = 1
return q
}()
ดังนั้นสำหรับชุดรหัสที่ใช้เวลานานโดยทั่วไปเราจะพูดว่า:
let task = BackgroundTaskOperation()
task.whatToDo = {
// do something here
}
backgroundTaskQueue.addOperation(task)
หากชุดรหัสที่ใช้เวลานานของคุณสามารถแบ่งออกเป็นขั้นตอนได้คุณอาจต้องยอมแพ้ก่อนหากงานของคุณถูกยกเลิก ในกรณีนั้นให้กลับมาก่อนเวลาอันควรจากการปิด โปรดทราบว่าการอ้างอิงของคุณไปยังงานจากภายในการปิดจะต้องอ่อนแอไม่เช่นนั้นคุณจะได้รับวงจรการรักษา นี่คือภาพประกอบเทียม:
let task = BackgroundTaskOperation()
task.whatToDo = { [weak task] in
guard let task = task else {return}
for i in 1...10000 {
guard !task.isCancelled else {return}
for j in 1...150000 {
let k = i*j
}
}
}
backgroundTaskQueue.addOperation(task)
ในกรณีที่คุณต้องล้างข้อมูลในกรณีที่งานเบื้องหลังถูกยกเลิกก่อนเวลาอันควรฉันได้จัดเตรียมcleanup
คุณสมบัติตัวจัดการที่เป็นทางเลือก(ไม่ได้ใช้ในตัวอย่างก่อนหน้านี้) คำตอบอื่น ๆ บางคำถูกวิพากษ์วิจารณ์ว่าไม่รวมถึงคำตอบนั้น