Jawaban singkat
Alih-alih mengakses self
secara langsung, Anda harus mengaksesnya secara tidak langsung, dari referensi yang tidak akan disimpan. Jika Anda tidak menggunakan Penghitungan Referensi Otomatis (ARC) , Anda dapat melakukan ini:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Kata __block
kunci menandai variabel yang dapat dimodifikasi di dalam blok (kami tidak melakukan itu) tetapi juga mereka tidak secara otomatis dipertahankan ketika blok tersebut dipertahankan (kecuali Anda menggunakan ARC). Jika Anda melakukan ini, Anda harus yakin bahwa tidak ada lagi yang akan mencoba untuk menjalankan blok setelah instance MyDataProcessor dirilis. (Mengingat struktur kode Anda, itu seharusnya tidak menjadi masalah.) Baca lebih lanjut tentang__block
.
Jika Anda menggunakan ARC , semantik __block
perubahan dan referensi akan dipertahankan, dalam hal ini Anda harus mendeklarasikannya __weak
.
Jawaban panjang
Katakanlah Anda memiliki kode seperti ini:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
Masalahnya di sini adalah diri mempertahankan referensi ke blok; sementara itu blok harus mempertahankan referensi ke diri sendiri untuk mengambil properti delegasi dan mengirim metode delegasi. Jika semua hal lain di aplikasi Anda melepaskan rujukannya ke objek ini, jumlah tetapnya tidak akan nol (karena blok mengarah ke sana) dan blok tidak melakukan kesalahan (karena objek menunjuk ke sana) dan seterusnya sepasang objek akan bocor ke tumpukan, menempati memori tetapi selamanya tidak dapat dijangkau tanpa debugger. Tragis, sungguh.
Kasing itu bisa dengan mudah diperbaiki dengan melakukan ini sebagai gantinya:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
Dalam kode ini, self mempertahankan blok, blok mempertahankan delegasi, dan tidak ada siklus (terlihat dari sini; delegasi dapat mempertahankan objek kami tapi itu di luar kendali kami saat ini). Kode ini tidak akan mengambil risiko kebocoran dengan cara yang sama, karena nilai properti delegasi ditangkap saat blok dibuat, alih-alih mendongak ketika dijalankan. Efek sampingnya adalah, jika Anda mengubah delegasi setelah blok ini dibuat, blok tersebut masih akan mengirim pesan pembaruan ke delegasi lama. Apakah itu mungkin terjadi atau tidak tergantung pada aplikasi Anda.
Meskipun Anda keren dengan perilaku itu, Anda masih tidak bisa menggunakan trik itu dalam kasus Anda:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
Di sini Anda mengirimkan self
langsung ke delegasi dalam pemanggilan metode, jadi Anda harus mendapatkannya di suatu tempat. Jika Anda memiliki kendali atas definisi tipe blok, hal terbaik adalah meneruskan delegasi ke blok sebagai parameter:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
Solusi ini menghindari siklus mempertahankan dan selalu memanggil delegasi saat ini.
Jika Anda tidak dapat mengubah blok, Anda bisa mengatasinya . Alasan mempertahankan siklus adalah peringatan, bukan kesalahan, adalah bahwa mereka tidak selalu mengeja malapetaka untuk aplikasi Anda. Jika MyDataProcessor
mampu melepaskan blok ketika operasi selesai, sebelum induknya akan mencoba melepaskannya, siklus akan rusak dan semuanya akan dibersihkan dengan benar. Jika Anda bisa yakin akan hal ini, maka hal yang benar untuk dilakukan adalah menggunakan a #pragma
untuk menekan peringatan untuk blok kode itu. (Atau gunakan flag kompiler per file. Tetapi jangan menonaktifkan peringatan untuk seluruh proyek.)
Anda juga bisa melihat menggunakan trik serupa di atas, menyatakan referensi lemah atau tidak dibatasi dan menggunakannya di blok. Sebagai contoh:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Ketiga hal di atas akan memberi Anda referensi tanpa mempertahankan hasilnya, meskipun mereka semua berperilaku sedikit berbeda: __weak
akan mencoba untuk nol referensi ketika objek dilepaskan; __unsafe_unretained
akan meninggalkan Anda dengan pointer yang tidak valid; __block
sebenarnya akan menambah tingkat tipuan lain dan memungkinkan Anda untuk mengubah nilai referensi dari dalam blok (tidak relevan dalam kasus ini, karena dp
tidak digunakan di tempat lain).
Apa yang terbaik akan tergantung pada kode apa yang dapat Anda ubah dan apa yang tidak bisa Anda ubah. Tapi semoga ini memberi Anda beberapa ide tentang bagaimana untuk melanjutkan.
dp
akan dirilis (misalnya jika itu adalah pengontrol tampilan dan dilepaskan), maka baris[dp.delegate ...
akan menyebabkan EXC_BADACCESS?strong
atauweak
?@weakify(..)
dan@strongify(...)
yang memungkinkan Anda untuk digunakanself
dalam blok dengan cara yang tidak mempertahankan.Ada juga opsi untuk menekan peringatan ketika Anda yakin bahwa siklus akan rusak di masa depan:
Dengan cara itu Anda tidak perlu bermain-main dengan
__weak
,self
aliasing dan awalan ivar eksplisit.sumber
__weak id weakSelf = self;
memiliki perilaku yang secara fundamental berbeda dari menekan peringatan. Pertanyaannya dimulai dengan "... jika Anda yakin siklus penahannya akan rusak"[array addObject:weakObject];
Jika OjectOutject telah dirilis, ini menyebabkan crash. Jelas itu tidak disukai daripada mempertahankan siklus. Anda harus memahami apakah blok Anda benar-benar hidup cukup lama untuk menjamin pelemahan, dan juga apakah Anda ingin tindakan di blok bergantung pada apakah objek yang lemah masih valid.Untuk solusi umum, saya mendefinisikan ini di header precompile. Menghindari pengambilan dan masih memungkinkan bantuan kompiler dengan menghindari penggunaan
id
Kemudian dalam kode dapat Anda lakukan:
sumber
self
di dalam blok Anda @weakify (mandiri); id block = ^ {@strongify (self); [self.delegate myAPIDidFinish: self]; };Saya percaya solusi tanpa ARC juga bekerja dengan ARC, menggunakan
__block
kata kunci:EDIT: Per Transisi ke Catatan Rilis ARC , objek yang dideklarasikan dengan
__block
penyimpanan masih dipertahankan. Gunakan__weak
(lebih disukai) atau__unsafe_unretained
(untuk kompatibilitas mundur).sumber
__block
kata kunci menghindari mempertahankan referensi itu. Terima kasih! Saya memperbarui jawaban monolitik saya. :-)Menggabungkan beberapa jawaban lain, inilah yang saya gunakan sekarang untuk diri yang diketik lemah untuk digunakan dalam blok:
Saya menetapkan itu sebagai Cuplikan Kode XCode dengan awalan penyelesaian "welf" dalam metode / fungsi, yang mengenai setelah mengetik hanya "kami".
sumber
peringatan => "menangkap diri di dalam blok cenderung memimpin siklus tetap"
ketika Anda merujuk diri atau propertinya di dalam blok yang sangat dipertahankan sendiri daripada yang ditunjukkan peringatan di atas.
jadi untuk menghindarinya kita harus membuatnya seminggu ref
jadi alih-alih menggunakan
kita harus gunakan
Catatan: mempertahankan siklus biasanya terjadi ketika beberapa bagaimana dua objek merujuk satu sama lain dimana keduanya memiliki jumlah referensi = 1 dan metode delloc mereka tidak pernah dipanggil.
sumber
Cara baru untuk melakukan ini adalah dengan menggunakan @weakify dan @strongify marco
Info Lebih Lanjut tentang @Beritahu @ Kuatkan Marco
sumber
Jika Anda yakin bahwa kode Anda tidak akan membuat siklus penyimpanan, atau bahwa siklus tersebut akan rusak kemudian, maka cara paling sederhana untuk membungkam peringatan itu adalah:
Alasan bahwa ini berfungsi adalah bahwa sementara akses titik properti diperhitungkan oleh analisis Xcode, dan karenanya
terlihat memiliki mempertahankan oleh xy (di sisi kiri penugasan) dan oleh xy (di sisi kanan), panggilan metode tidak tunduk pada analisis yang sama, bahkan ketika mereka panggilan properti-metode akses yang setara dengan akses-titik, bahkan ketika metode-metode akses properti dihasilkan oleh kompiler, begitu juga
hanya sisi kanan yang dilihat sebagai membuat retain (dengan y dari x), dan tidak ada peringatan siklus retain yang dihasilkan.
sumber