Mengapa delegasi Objective-C biasanya diberi properti alih-alih mempertahankan?

176

Saya berselancar melalui blog indah yang dikelola oleh Scott Stevenson, dan saya mencoba memahami konsep Objective-C yang mendasar dalam menugaskan delegasi properti 'assign' vs 'retain'. Catatan, keduanya sama di lingkungan pengumpulan sampah. Saya sebagian besar peduli dengan lingkungan berbasis non-GC (misalnya: iPhone).

Langsung dari blog Scott:

"Kata kunci yang ditetapkan akan menghasilkan setter yang memberikan nilai ke variabel instan secara langsung, daripada menyalin atau mempertahankannya. Ini terbaik untuk tipe primitif seperti NSInteger dan CGFloat, atau objek yang tidak Anda miliki secara langsung, seperti delegasi."

Apa artinya Anda tidak secara langsung memiliki objek delegasi? Saya biasanya mempertahankan delegasi saya, karena jika saya tidak ingin mereka pergi ke jurang, mempertahankan itu akan mengurusnya untuk saya. Saya biasanya abstrak UITableViewController jauh dari masing-masing dataSource dan mendelegasikan juga. Saya juga mempertahankan objek tertentu. Saya ingin memastikan itu tidak pernah hilang sehingga UITableView saya selalu memiliki delegasinya.

Dapatkah seseorang menjelaskan lebih lanjut di mana / mengapa saya salah, jadi saya bisa memahami paradigma umum ini dalam pemrograman Objective-C 2.0 menggunakan properti assign pada delegasi alih-alih mempertahankan?

Terima kasih!

Plumenator
sumber
Retag dengan "delegasi" dan tanpa "iphone".
Quinn Taylor
mengapa delegate assign bukannya copy (seperti NSString?)
OMGPOP

Jawaban:

175

Alasan Anda menghindari mempertahankan delegasi adalah karena Anda harus menghindari siklus penyimpanan:

A menciptakan B A menetapkan dirinya sebagai delegasi B… A dilepaskan oleh pemiliknya

Jika B mempertahankan A, A tidak akan dilepaskan, karena B memiliki A, sehingga dealloc A tidak akan pernah dipanggil, menyebabkan A dan B bocor.

Anda tidak perlu khawatir tentang A pergi karena memiliki B dan dengan demikian menghilangkannya dalam dealloc.

Andrew Pouliot
sumber
Saya tidak setuju, Mike. Saya baru saja menemukan masalah di mana modal memiliki delegasi yang menolak modal. Tetapi ketika saya melakukan peringatan memori di modal, ia melepaskan delegasi. Kemudian ketika saya pergi untuk menghilangkan modal saya, delegasi tidak ada. Jatuh.
Paul Shapiro
Ok jadi saya tidak setuju, tapi Anda benar itu cacat desain. Saya mengetahui bahwa saya meneruskan panggilan pemecatan saya melalui kelas anak dari pemecatan yang sebenarnya. Kelas anak itu dilepaskan dan tidak bisa diteruskan ke delegasi wadah modal untuk diberhentikan. Saya mengubahnya untuk memberikan pointer ke delegasi akhir dan yang tidak dirilis pada peringatan memori dan semuanya baik-baik saja
Paul Shapiro
2
Kode Anda tidak boleh ditulis sedemikian rupa sehingga delegasi nil menyebabkannya mogok. Hanya objek yang memiliki yang memiliki referensi memiliki. Ketika dealloc'd, itu harus mengatur delegasi objek yang dimiliki menjadi nol sebelum melepaskannya. Maka pesan yang dikirim ke delegasi nil diabaikan saja. Melewati objek nil dalam sebuah pesan bisa macet. Pastikan Anda tidak berurusan dengan delegasi seperti itu.
David Gish
Tunggu - bukankah itu weakartinya? Pertanyaannya adalah mengapa menggunakan assignbukan weak?
wcochran
3
@wcochran: Tidak, pertanyaan ini mengapa menggunakan assignbukan retain. Pertanyaannya lebih tua dari ARC; weakdan strong(yang terakhir identik dengan retain) tidak ada sampai ARC diperkenalkan. Anda harus menanyakan pertanyaan Anda tentang weakvs. assignsecara terpisah.
Peter Hosey
44

Karena objek yang mengirim pesan delegasi tidak memiliki delegasi.

Sering kali, itu sebaliknya, seperti ketika controller menetapkan dirinya sebagai delegasi dari view atau window: controller memiliki view / window, jadi jika view / window memiliki delegate, kedua objek akan saling memiliki. Ini, tentu saja, adalah siklus penahan, mirip dengan kebocoran dengan konsekuensi yang sama (benda yang seharusnya mati tetap hidup).

Di lain waktu, objeknya adalah rekan-rekan: tidak ada yang memiliki yang lain, mungkin karena keduanya dimiliki oleh objek ketiga yang sama.

Either way, objek dengan delegasi tidak boleh mempertahankan delegasinya.

(Ngomong-ngomong, setidaknya ada satu pengecualian. Aku tidak ingat apa itu, dan kurasa tidak ada alasan bagus untuk itu.)


Tambahan (ditambahkan 2012-05-19): Di bawah ARC, Anda harus menggunakan weakbukan assign. Referensi yang lemah diatur nilsecara otomatis ketika objek mati, menghilangkan kemungkinan bahwa objek delegasi akhirnya mengirim pesan ke delegasi mati.

Jika Anda menjauh dari ARC karena suatu alasan, setidaknya ubah assignproperti yang menunjuk ke objek unsafe_unretained, yang secara eksplisit menyatakan bahwa ini adalah referensi yang tidak dibatasi tetapi tidak memusatkan perhatian pada suatu objek.

assign tetap sesuai untuk nilai-nilai non-objek di bawah ARC dan MRC.

Peter Hosey
sumber
13
NSURLConnectionmempertahankan delegasinya.
Ya, gunakan weak, tetapi itu tidak menjawab pertanyaan asli: Mengapa Apple menggunakan assignbukan weak?
wcochran
@wcochran: Pertanyaan aslinya adalah "mengapa properti delegasi diberikan assignalih-alih dipertahankan "; weaktidak ada saat ditanya. Pertanyaan Anda berbeda, yang harus Anda tanyakan secara terpisah. Saya akan dengan senang hati menjawabnya.
Peter Hosey
@wcochran dan Peter, sudahkah pertanyaan itu ditanyakan di tempat lain?
Tuan Rogers
17

Perhatikan bahwa ketika Anda memiliki delegasi yang ditugaskan, itu membuatnya sangat penting untuk selalu menetapkan nilai delegasi itu menjadi nol setiap kali objek akan dideallocated - jadi objek harus selalu berhati-hati untuk mencari referensi delegasi di dealloc jika belum melakukannya di tempat lain.

Kendall Helmstetter Gelner
sumber
"Perhatikan bahwa ketika Anda memiliki delegasi yang ditugaskan, itu membuatnya sangat penting untuk selalu menetapkan nilai delegasi itu menjadi nol setiap kali objek akan dideallocated" Mengapa?
Peter Hosey
2
Karena referensi apa pun yang ditetapkan, akan menjadi tidak valid setelah suatu objek dideallocated (menunjuk pada memori tidak lagi dialokasikan ke jenis objek yang diharapkan) - dan dengan demikian menyebabkan crash jika Anda mencoba menggunakannya. Tanda ini dalam debugger adalah ketika debugger mengklaim beberapa variabel memiliki tipe yang tampaknya benar-benar salah dari variabel yang sebenarnya dinyatakan.
Kendall Helmstetter Gelner
1
Ini hanya diperlukan jika objek tempat Anda didelegasikan oleh sumber lain, seperti timer atau panggilan balik asinkron lainnya. Kalau tidak, itu akan dibatalkan alokasi setelah Anda melepaskannya dan tidak akan mencoba memanggil metode delegasi.
Andrew Pouliot
@Andrew: Itu benar, tetapi jika Anda selalu melakukan praktik untuk menghilangkan delegasi maka Anda tidak akan lupa ketika itu penting, atau jika Anda secara tidak sengaja mempertahankan benda yang ditahan dan benda itu tetap ada. Jika Anda menemukan delegasi maka hasilnya hanyalah kebocoran, bukan kebocoran yang diikuti oleh tabrakan.
Kendall Helmstetter Gelner
1

Salah satu alasan di balik itu adalah untuk menghindari siklus mempertahankan. Hanya untuk menghindari skenario di mana A dan B keduanya saling referensi satu sama lain dan tidak ada yang dilepaskan dari memori.

Acutally menetapkan yang terbaik untuk tipe primitif seperti NSInteger dan CGFloat, atau benda Anda tidak langsung sendiri, seperti delegasi.

Puneet Sharma
sumber
Itu agak disalin dari kutipan OP dan masing-masing jawaban yang diterima, bukan?
dakab