Saya agak bingung bagaimana dan kapan harus menggunakannya beginBackgroundTaskWithExpirationHandler
.
Apple menunjukkan dalam contoh mereka untuk menggunakannya dalam applicationDidEnterBackground
delegasi, untuk mendapatkan lebih banyak waktu untuk menyelesaikan beberapa tugas penting, biasanya transaksi jaringan.
Saat melihat aplikasi saya, sepertinya sebagian besar jaringan saya penting, dan saat dimulai, saya ingin menyelesaikannya jika pengguna menekan tombol beranda.
Jadi apakah diterima / praktik yang baik untuk membungkus setiap transaksi jaringan (dan saya tidak berbicara tentang mengunduh sebagian besar data, sebagian besar beberapa xml pendek) dengan beginBackgroundTaskWithExpirationHandler
aman?
Jawaban:
Jika Anda ingin transaksi jaringan Anda berlanjut di latar belakang, Anda harus membungkusnya di tugas latar belakang. Sangat penting juga bagi Anda untuk menelepon
endBackgroundTask
setelah selesai - jika tidak, aplikasi akan mati setelah waktu yang ditentukan telah kedaluwarsa.Punyaku cenderung terlihat seperti ini:
Saya memiliki
UIBackgroundTaskIdentifier
properti untuk setiap tugas latar belakangKode yang setara di Swift
sumber
Jawaban yang diterima sangat membantu dan seharusnya baik-baik saja dalam banyak kasus, namun ada dua hal yang mengganggu saya tentang hal itu:
Seperti yang telah dicatat oleh sejumlah orang, menyimpan pengenal tugas sebagai properti berarti dapat ditimpa jika metode dipanggil beberapa kali, yang mengarah ke tugas yang tidak akan pernah berakhir dengan baik hingga dipaksa untuk diakhiri oleh OS pada saat kedaluwarsa .
Pola ini memerlukan properti unik untuk setiap panggilan
beginBackgroundTaskWithExpirationHandler
yang tampaknya rumit jika Anda memiliki aplikasi yang lebih besar dengan banyak metode jaringan.Untuk mengatasi masalah ini, saya menulis sebuah singleton yang menangani semua pipa ledeng dan melacak tugas aktif dalam kamus. Tidak ada properti yang diperlukan untuk melacak pengidentifikasi tugas. Sepertinya bekerja dengan baik. Penggunaan disederhanakan menjadi:
Secara opsional, jika Anda ingin menyediakan blok penyelesaian yang melakukan sesuatu selain mengakhiri tugas (yang sudah ada di dalamnya), Anda dapat memanggil:
Kode sumber yang relevan tersedia di bawah ini (hal-hal tunggal dikecualikan agar singkatnya). Komentar / umpan balik diterima.
sumber
typedef
CompletionBlock? Sederhananya ini:typedef void (^CompletionBlock)();
Berikut adalah kelas Swift yang merangkum menjalankan tugas latar belakang:
Cara termudah untuk menggunakannya:
Jika Anda perlu menunggu callback delegasi sebelum Anda mengakhiri, gunakan sesuatu seperti ini:
sumber
begin
metode ini, tetapi mudah untuk melihat bagaimana menambahkan fitur itu.Seperti disebutkan di sini dan dalam jawaban atas pertanyaan SO lainnya, Anda TIDAK ingin menggunakan
beginBackgroundTask
hanya saat aplikasi Anda akan masuk ke latar belakang; sebaliknya, Anda harus menggunakan tugas latar belakang untuk setiap operasi memakan waktu yang selesai Anda ingin memastikan bahkan jika aplikasi tidak masuk ke latar belakang.Oleh karena itu, kode Anda kemungkinan besar akan dibumbui dengan pengulangan kode boilerplate yang sama untuk memanggil
beginBackgroundTask
dan secaraendBackgroundTask
koheren. Untuk mencegah pengulangan ini, tentu masuk akal untuk ingin mengemas boilerplate menjadi beberapa entitas terenkapsulasi tunggal.Saya suka beberapa jawaban yang ada untuk melakukan itu, tetapi menurut saya cara terbaik adalah dengan menggunakan subkelas Operasi:
Anda dapat memasukkan Operation ke dalam OperationQueue mana pun dan memanipulasi antrean itu sesuai keinginan Anda. Misalnya, Anda bebas membatalkan sebelum waktunya semua operasi yang ada di antrian.
Jika Anda memiliki lebih dari satu hal yang harus dilakukan, Anda dapat merangkai beberapa Operasi tugas latar belakang. Dependensi dukungan operasi.
Operation Queue dapat (dan seharusnya) menjadi antrian latar belakang; jadi, tidak perlu khawatir untuk melakukan kode asinkron di dalam tugas Anda, karena Operasi adalah kode asinkron. (Memang, tidak masuk akal untuk mengeksekusi level lain dari kode asinkron di dalam Operasi, karena Operasi akan selesai bahkan sebelum kode itu dapat dimulai. Jika Anda perlu melakukannya, Anda akan menggunakan Operasi lain.)
Berikut subclass Operation yang mungkin:
Seharusnya jelas bagaimana menggunakan ini, tetapi jika tidak, bayangkan kita memiliki OperationQueue global:
Jadi untuk kumpulan kode yang memakan waktu, kami akan mengatakan:
Jika kumpulan kode Anda yang memakan waktu dapat dibagi menjadi beberapa tahap, Anda mungkin ingin mundur lebih awal jika tugas Anda dibatalkan. Dalam hal ini, kembalilah sebelum waktunya dari penutupan. Perhatikan bahwa referensi Anda ke tugas dari dalam closure harus lemah atau Anda akan mendapatkan siklus retensi. Berikut ilustrasi buatannya:
Jika Anda memiliki pembersihan yang harus dilakukan jika tugas latar belakang itu sendiri dibatalkan sebelum waktunya, saya telah menyediakan
cleanup
properti penangan opsional (tidak digunakan dalam contoh sebelumnya). Beberapa jawaban lain dikritik karena tidak memasukkan itu.sumber
Saya menerapkan solusi Joel. Berikut kode lengkapnya:
file .h:
file .m:
sumber