Saya telah mendengar orang mengatakan bahwa metode swizzling adalah praktik yang berbahaya. Bahkan nama swizzling menunjukkan bahwa itu sedikit curang.
Metode Swizzling adalah memodifikasi pemetaan sehingga memanggil pemilih A benar-benar akan memanggil implementasi B. Salah satu penggunaannya adalah untuk memperluas perilaku kelas sumber tertutup.
Bisakah kita memformalisasikan risiko sehingga siapa pun yang memutuskan apakah akan menggunakan swizzling dapat membuat keputusan yang tepat apakah layak untuk apa yang mereka coba lakukan.
Misalnya
- Penamaan Tabrakan : Jika kelas nanti memperluas fungsinya untuk memasukkan nama metode yang telah Anda tambahkan, itu akan menyebabkan masalah besar. Mengurangi risiko dengan penamaan metode swizzled yang masuk akal.
ios
objective-c
swizzling
Robert
sumber
sumber
Jawaban:
Saya pikir ini adalah pertanyaan yang sangat hebat, dan ini memalukan bahwa alih-alih menangani pertanyaan yang sebenarnya, sebagian besar jawaban telah mengacaukan masalah dan hanya mengatakan untuk tidak menggunakan swizzling.
Menggunakan metode mendesis seperti menggunakan pisau tajam di dapur. Beberapa orang takut pisau tajam karena mereka pikir mereka akan memotong diri mereka sendiri dengan buruk, tetapi kenyataannya pisau tajam lebih aman .
Metode swizzling dapat digunakan untuk menulis kode yang lebih baik, lebih efisien, lebih dapat dipelihara. Itu juga dapat disalahgunakan dan menyebabkan bug yang mengerikan.
Latar Belakang
Seperti halnya semua pola desain, jika kita sepenuhnya menyadari konsekuensi dari pola tersebut, kita dapat membuat keputusan yang lebih tepat tentang apakah akan menggunakannya atau tidak. Lajang adalah contoh yang baik dari sesuatu yang cukup kontroversial, dan untuk alasan yang baik - mereka benar-benar sulit diimplementasikan dengan benar. Banyak orang masih memilih untuk menggunakan lajang. Hal yang sama dapat dikatakan tentang swizzling. Anda harus membentuk opini Anda sendiri setelah Anda sepenuhnya memahami yang baik dan yang buruk.
Diskusi
Berikut adalah beberapa perangkap metode swizzling:
Poin-poin ini semuanya valid, dan dalam mengatasinya kita dapat meningkatkan pemahaman kita tentang metode swizzling serta metodologi yang digunakan untuk mencapai hasil. Saya akan mengambil masing-masing sekaligus.
Metode swizzling bukan atom
Saya belum melihat implementasi metode swizzling yang aman digunakan bersamaan 1 . Ini sebenarnya bukan masalah di 95% kasus yang Anda ingin menggunakan metode swizzling. Biasanya, Anda hanya ingin mengganti implementasi suatu metode, dan Anda ingin implementasi itu digunakan untuk seumur hidup program Anda. Ini berarti Anda harus melakukan metode Anda
+(void)load
. Theload
metode kelas dijalankan secara serial pada awal aplikasi Anda. Anda tidak akan memiliki masalah dengan konkurensi jika Anda melakukan swizzling di sini. Namun, jika Anda gagal+(void)initialize
, Anda bisa berakhir dengan kondisi balapan dalam implementasi swizzling Anda dan runtime bisa berakhir dalam keadaan aneh.Mengubah perilaku kode yang tidak dimiliki
Ini adalah masalah dengan swizzling, tapi ini semacam intinya. Tujuannya adalah untuk dapat mengubah kode itu. Alasan orang menunjukkan ini sebagai masalah besar adalah karena Anda tidak hanya mengubah hal-hal untuk satu hal
NSButton
yang ingin Anda ubah, tetapi sebagai gantinya untuk semuaNSButton
kejadian dalam aplikasi Anda. Untuk alasan ini, Anda harus berhati-hati saat berdecak, tetapi Anda tidak perlu menghindarinya sama sekali.Pikirkan seperti ini ... jika Anda mengganti metode di kelas dan Anda tidak memanggil metode kelas super, Anda dapat menyebabkan masalah muncul. Dalam kebanyakan kasus, kelas super mengharapkan metode itu dipanggil (kecuali didokumentasikan sebaliknya). Jika Anda menerapkan pemikiran yang sama pada swizzling, Anda telah membahas sebagian besar masalah. Selalu panggil implementasi asli. Jika tidak, Anda mungkin berubah terlalu banyak untuk aman.
Kemungkinan konflik penamaan
Konflik penamaan adalah masalah di seluruh Kakao. Kami sering mengawali nama kelas dan nama metode dalam kategori. Sayangnya, konflik penamaan adalah wabah dalam bahasa kita. Dalam kasus swizzling, mereka tidak harus seperti itu. Kita hanya perlu mengubah cara kita berpikir tentang metode yang sedikit meliuk-liuk. Kebanyakan swizzling dilakukan seperti ini:
Ini berfungsi dengan baik, tetapi apa yang akan terjadi jika
my_setFrame:
didefinisikan di tempat lain? Masalah ini tidak unik untuk swizzling, tetapi kita bisa mengatasinya. Solusinya memiliki manfaat tambahan untuk mengatasi perangkap lain juga. Inilah yang kami lakukan sebagai gantinya:Meskipun ini terlihat sedikit kurang seperti Objective-C (karena menggunakan pointer fungsi), ia menghindari konflik penamaan. Pada prinsipnya, ia melakukan hal yang sama persis seperti swizzling standar. Ini mungkin sedikit perubahan bagi orang yang telah menggunakan swizzling seperti yang telah didefinisikan untuk sementara waktu, tetapi pada akhirnya, saya pikir itu lebih baik. Metode swizzling didefinisikan sebagai berikut:
Berputar dengan mengubah nama metode mengubah argumen metode ini
Ini yang terbesar di pikiran saya. Inilah alasan mengapa metode standar swizzling tidak boleh dilakukan. Anda mengubah argumen yang diteruskan ke implementasi metode asli. Di sinilah terjadi:
Apa yang dilakukan garis ini adalah:
Yang akan menggunakan runtime untuk mencari implementasi
my_setFrame:
. Setelah implementasi ditemukan, ia memanggil implementasi dengan argumen yang sama yang diberikan. Implementasi yang ditemukannya adalah implementasi aslisetFrame:
, jadi berjalan di depan dan memanggil itu, tetapi_cmd
argumennya tidaksetFrame:
seperti yang seharusnya. Sekarangmy_setFrame:
. Implementasi asli dipanggil dengan argumen yang tidak pernah diharapkan akan diterima. Ini tidak baik.Ada solusi sederhana - gunakan teknik swizzling alternatif yang didefinisikan di atas. Argumen akan tetap tidak berubah!
Urutan swizzles penting
Urutan metode mendapatkan hal-hal yang membingungkan. Dengan asumsi
setFrame:
hanya didefinisikanNSView
, bayangkan urutan hal-hal ini:Apa yang terjadi ketika metode pada
NSButton
swizzled? Yah sebagian besar swizzling akan memastikan bahwa itu tidak menggantikan implementasisetFrame:
untuk semua tampilan, sehingga akan memunculkan metode instance. Ini akan menggunakan implementasi yang ada untuk mendefinisikan ulangsetFrame:
diNSButton
kelas sehingga pertukaran implementasi tidak mempengaruhi semua tampilan. Implementasi yang ada adalah yang didefinisikanNSView
. Hal yang sama akan terjadi ketika swizzling onNSControl
(lagi menggunakanNSView
implementasi).Ketika Anda memanggil
setFrame:
sebuah tombol, karena itu ia akan memanggil metode swizzled Anda, dan kemudian melompat langsung kesetFrame:
metode yang awalnya didefinisikanNSView
. TheNSControl
danNSView
swizzled implementasi tidak akan dipanggil.Tapi bagaimana jika pesanannya adalah:
Karena pandangan swizzling terjadi terlebih dahulu, kontrol swizzling akan dapat menarik metode yang tepat. Demikian juga, karena kontrol swizzling adalah sebelum tombol swizzling, tombol akan menarik implementasi swizzled kontrol
setFrame:
. Ini agak membingungkan, tetapi ini adalah urutan yang benar. Bagaimana kita memastikan urutan ini?Sekali lagi, cukup gunakan
load
untuk memutar hal-hal. Jika Anda masukload
dan hanya membuat perubahan pada kelas yang sedang dimuat, Anda akan aman. Theload
Metode jaminan bahwa metode yang super beban kelas akan dipanggil sebelum subclass. Kami akan mendapatkan pesanan yang tepat dan tepat!Sulit dipahami (terlihat rekursif)
Melihat metode swizzled yang didefinisikan secara tradisional, saya pikir sangat sulit untuk mengatakan apa yang terjadi. Tetapi melihat cara alternatif yang telah kami lakukan di atas, cukup mudah untuk dipahami. Yang ini sudah dipecahkan!
Sulit di-debug
Salah satu kebingungan selama debugging adalah melihat backtrace aneh di mana nama-nama swizzled dicampuradukkan dan semuanya tercampur aduk di kepala Anda. Sekali lagi, implementasi alternatif membahas hal ini. Anda akan melihat fungsi yang diberi nama dengan jelas di backtraces. Namun, swizzling dapat menjadi sulit untuk di-debug karena sulit untuk mengingat apa dampak dari swizzling. Dokumentasikan kode Anda dengan baik (walaupun Anda pikir hanya Anda yang akan melihatnya). Ikuti praktik yang baik, dan Anda akan baik-baik saja. Tidak sulit untuk melakukan debug daripada kode multi-threaded.
Kesimpulan
Metode swizzling aman jika digunakan dengan benar. Tindakan keamanan sederhana yang dapat Anda lakukan adalah hanya masuk
load
. Seperti banyak hal dalam pemrograman, ini bisa berbahaya, tetapi memahami konsekuensinya akan memungkinkan Anda menggunakannya dengan benar.1 Dengan menggunakan metode swizzling yang didefinisikan di atas, Anda dapat membuat thread aman jika Anda menggunakan trampolin. Anda akan membutuhkan dua trampolin. Pada awal metode, Anda harus menetapkan penunjuk fungsi
store
,, ke fungsi yang berputar sampai alamat yangstore
menunjuk berubah. Ini akan menghindari kondisi balapan di mana metode swizzled dipanggil sebelum Anda dapat mengaturstore
pointer fungsi. Anda kemudian perlu menggunakan trampolin dalam kasus di mana implementasinya belum didefinisikan di kelas dan memiliki pencarian trampolin dan memanggil metode kelas super dengan benar. Menentukan metode sehingga secara dinamis mencari implementasi super akan memastikan bahwa urutan panggilan swizzling tidak masalah.sumber
Pertama saya akan mendefinisikan dengan tepat apa yang saya maksud dengan metode swizzling:
Metode swizzling lebih umum daripada ini, tetapi ini adalah kasus yang saya minati.
Bahaya:
Perubahan di kelas asli . Kami tidak memiliki kelas yang sedang kami geliat. Jika kelas berubah swizzle kami mungkin berhenti bekerja.
Sulit dipertahankan . Anda tidak hanya harus menulis dan memelihara metode swizzled. Anda harus menulis dan memelihara kode yang membentuk swizzle sebelumnya
Sulit di-debug . Sulit untuk mengikuti aliran swizzle, beberapa orang bahkan mungkin tidak menyadari swizzle telah terbentuk sebelumnya. Jika ada bug yang diperkenalkan dari swizzle (mungkin karena perubahan di kelas asli) mereka akan sulit untuk diselesaikan.
Singkatnya, Anda harus tetap melakukan swizzling seminimal mungkin dan mempertimbangkan bagaimana perubahan di kelas asli dapat mempengaruhi swizzle Anda. Anda juga harus berkomentar dan mendokumentasikan dengan jelas apa yang Anda lakukan (atau hanya menghindarinya sepenuhnya).
sumber
Bukan swizzling itu sendiri yang benar-benar berbahaya. Masalahnya adalah, seperti yang Anda katakan, itu sering digunakan untuk memodifikasi perilaku kelas kerangka kerja. Dengan asumsi Anda tahu sesuatu tentang cara kerja kelas privat itu "berbahaya." Bahkan jika modifikasi Anda bekerja hari ini, selalu ada peluang bahwa Apple akan mengubah kelas di masa depan dan menyebabkan modifikasi Anda rusak. Juga, jika banyak aplikasi berbeda melakukannya, itu membuat Apple jauh lebih sulit untuk mengubah kerangka kerja tanpa merusak banyak perangkat lunak yang ada.
sumber
Digunakan dengan hati-hati dan bijak, itu dapat menyebabkan kode yang elegan, tetapi biasanya, itu hanya mengarah pada kode yang membingungkan.
Saya mengatakan bahwa itu harus dilarang, kecuali Anda kebetulan tahu bahwa itu memberikan kesempatan yang sangat elegan untuk tugas desain tertentu, tetapi Anda perlu tahu dengan jelas mengapa itu berlaku dengan baik untuk situasi, dan mengapa alternatif tidak bekerja secara elegan untuk situasi tersebut. .
Misalnya, salah satu aplikasi yang baik dari metode swizzling adalah isa swizzling, yaitu bagaimana ObjC mengimplementasikan Key Value Observing.
Contoh yang buruk mungkin bergantung pada metode swizzling sebagai cara untuk memperluas kelas Anda, yang mengarah pada penggandaan yang sangat tinggi.
sumber
Meskipun saya telah menggunakan teknik ini, saya ingin menunjukkan bahwa:
sumber
Saya merasa bahwa bahaya terbesar adalah menciptakan banyak efek samping yang tidak diinginkan, sepenuhnya secara tidak sengaja. Efek samping ini dapat muncul sebagai 'bug' yang pada gilirannya membawa Anda ke jalan yang salah untuk menemukan solusinya. Dalam pengalaman saya, bahayanya adalah kode yang tidak terbaca, membingungkan, dan membuat frustrasi. Seperti ketika seseorang menggunakan pointer fungsi secara berlebihan di C ++.
sumber
Anda mungkin berakhir dengan kode yang terlihat aneh
dari kode produksi aktual yang terkait dengan beberapa keajaiban UI.
sumber
Metode swizzling bisa sangat membantu dalam pengujian unit.
Hal ini memungkinkan Anda untuk menulis objek tiruan dan memiliki objek tiruan yang digunakan sebagai ganti objek nyata. Kode Anda tetap bersih dan unit test Anda memiliki perilaku yang dapat diprediksi. Katakanlah Anda ingin menguji beberapa kode yang menggunakan CLLocationManager. Tes unit Anda dapat melemahkan startUpdatingLocation sehingga akan memberi makan set lokasi yang telah ditentukan untuk delegasi Anda dan kode Anda tidak perlu berubah.
sumber