Saya perlu menyembunyikan (merahasiakan) -init
metode kelas saya di Objective-C.
Bagaimana saya bisa melakukan itu?
objective-c
lajos
sumber
sumber
NS_UNAVAILABLE
. Saya biasanya mendesak Anda untuk menggunakan pendekatan ini. Apakah OP akan mempertimbangkan untuk merevisi jawaban yang mereka terima? Jawaban lain di sini memberikan banyak detail berguna, tetapi bukan metode yang disukai untuk mencapai ini.NS_UNAVAILABLE
masih memungkinkan penelepon untuk memanggilinit
secara tidak langsung melaluinew
. Cukup menggantiinit
untuk kembalinil
akan menangani kedua kasus.Jawaban:
Objective-C, seperti Smalltalk, tidak memiliki konsep metode "pribadi" versus "publik". Pesan apa pun dapat dikirim ke objek apa saja kapan saja.
Apa yang dapat Anda lakukan adalah melempar
NSInternalInconsistencyException
jika-init
metode Anda dipanggil:Alternatif lain - yang mungkin jauh lebih baik dalam praktiknya - adalah membuat
-init
melakukan sesuatu yang masuk akal untuk kelas Anda jika memungkinkan.Jika Anda mencoba melakukan ini karena Anda mencoba "memastikan" objek tunggal digunakan, jangan repot-repot. Secara khusus, tidak repot-repot dengan "override
+allocWithZone:
,-init
,-retain
,-release
" metode menciptakan lajang. Ini sebenarnya selalu tidak perlu dan hanya menambah kerumitan tanpa keuntungan nyata yang nyata.Alih-alih, tulis saja kode Anda sedemikian rupa sehingga
+sharedWhatever
metode Anda adalah bagaimana Anda mengakses singleton, dan mendokumentasikannya sebagai cara untuk mendapatkan instance singleton di header Anda. Hanya itulah yang Anda butuhkan di sebagian besar kasus.sumber
alloc
daninit
dan memiliki fungsi kode mereka salah karena mereka memiliki kelas yang tepat, tetapi contoh yang salah. Ini adalah esensi dari prinsip enkapsulasi dalam OO. Anda menyembunyikan hal-hal di API Anda yang tidak perlu, atau dapatkan, diakses oleh kelas lain. Anda tidak hanya membiarkan semuanya publik dan mengharapkan manusia untuk melacak semuanya.NS_UNAVAILABLE
Ini adalah versi singkat dari atribut yang tidak tersedia. Ini pertama kali muncul di macOS 10.7 dan iOS 5 . Ini didefinisikan dalam NSObjCRuntime.h sebagai
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
.Ada versi yang menonaktifkan metode ini hanya untuk klien Swift , bukan untuk kode ObjC:
unavailable
Tambahkan
unavailable
atribut ke header untuk menghasilkan kesalahan kompiler pada panggilan apa pun untuk init.Jika Anda tidak memiliki alasan, ketikkan saja
__attribute__((unavailable))
, atau bahkan__unavailable
:doesNotRecognizeSelector:
Gunakan
doesNotRecognizeSelector:
untuk meningkatkan NSInvalidArgumentException. "Sistem runtime memanggil metode ini setiap kali suatu objek menerima pesan aSelector yang tidak bisa ditanggapi atau diteruskan."NSAssert
Gunakan
NSAssert
untuk melempar NSInternalInconsistencyException dan menampilkan pesan:raise:format:
Gunakan
raise:format:
untuk melempar pengecualian Anda sendiri:[self release]
diperlukan karena objek sudahalloc
terpasang. Saat menggunakan ARC, kompiler akan memanggilnya untuk Anda. Bagaimanapun, bukan sesuatu yang perlu dikhawatirkan ketika Anda akan menghentikan eksekusi dengan sengaja.objc_designated_initializer
Jika Anda bermaksud menonaktifkan
init
untuk memaksa penggunaan penginisialisasi yang ditunjuk, ada atribut untuk itu:Ini menghasilkan peringatan kecuali metode inisialisasi lainnya memanggil secara
myOwnInit
internal. Detail akan dipublikasikan di Adopting Modern Objective-C setelah rilis Xcode berikutnya (saya kira).sumber
init
. Karena, jika metode ini tidak valid, mengapa Anda menginisialisasi objek? Plus, saat melempar pengecualian, Anda akan dapat menentukan beberapa pesan khusus yang mengomunikasikaninit*
metode yang benar kepada pengembang, sementara Anda tidak memiliki opsi seperti itu jika terjadidoesNotRecognizeSelector
.Apple telah mulai menggunakan yang berikut ini dalam file header mereka untuk menonaktifkan konstruktor init:
Ini ditampilkan dengan benar sebagai kesalahan kompiler di Xcode. Secara khusus, ini diatur dalam beberapa file header HealthKit mereka (HKUnit adalah salah satunya).
sumber
Jika Anda berbicara tentang metode default -it maka Anda tidak bisa. Ini diwarisi dari NSObject dan setiap kelas akan menanggapinya tanpa peringatan.
Anda bisa membuat metode baru, katakan -initMyClass, dan letakkan dalam kategori pribadi seperti yang disarankan Matt. Kemudian tentukan metode default -init untuk menaikkan pengecualian jika dipanggil atau (lebih baik) memanggil private -initMyClass Anda dengan beberapa nilai default.
Salah satu alasan utama orang tampaknya ingin menyembunyikan init adalah untuk objek tunggal . Jika itu masalahnya maka Anda tidak perlu menyembunyikan -init, cukup kembalikan objek singleton saja (atau buat jika belum ada).
sumber
Masukkan ini dalam file header
sumber
Anda dapat mendeklarasikan metode apa pun yang tidak tersedia menggunakan
NS_UNAVAILABLE
.Jadi Anda bisa meletakkan baris-baris ini di bawah @interface Anda
Bahkan lebih baik mendefinisikan makro di header awalan Anda
dan
sumber
Itu tergantung pada apa yang Anda maksud dengan "jadikan pribadi". Dalam Objective-C, memanggil metode pada objek mungkin lebih baik digambarkan sebagai mengirim pesan ke objek itu. Tidak ada dalam bahasa yang melarang klien memanggil metode apa pun pada objek; yang terbaik yang dapat Anda lakukan adalah tidak mendeklarasikan metode dalam file header. Jika klien tetap memanggil metode "pribadi" dengan tanda tangan yang tepat, itu masih akan dijalankan pada saat runtime.
Yang mengatakan, cara paling umum untuk membuat metode pribadi di Objective-C adalah membuat Kategori dalam file implementasi, dan mendeklarasikan semua metode "tersembunyi" di sana. Ingat bahwa ini tidak akan benar-benar mencegah panggilan untuk tidak
init
berjalan, tetapi kompiler akan memuntahkan peringatan jika ada yang mencoba melakukan ini.MyClass.m
Ada utas yang layak di MacRumors.com tentang topik ini.
sumber
baik masalah mengapa Anda tidak dapat membuatnya "pribadi / tidak terlihat" adalah karena metode init akan dikirim ke id (karena alokasi mengembalikan id) tidak ke YourClass
Perhatikan bahwa dari titik kompiler (pemeriksa) id dapat berpotensi merespons apa pun yang pernah diketik (ia tidak dapat memeriksa apa yang benar-benar masuk ke id saat runtime), sehingga Anda bisa menyembunyikannya hanya ketika tidak ada tempat (publik = dalam publik header) gunakan metode init, daripada kompilasi akan tahu, bahwa tidak ada cara bagi id untuk menanggapi init, karena tidak ada init di mana saja (di sumber Anda, semua libs dll ...)
jadi Anda tidak dapat melarang pengguna untuk melewatkan init dan dihancurkan oleh kompiler ... tetapi yang dapat Anda lakukan adalah mencegah pengguna mendapatkan contoh nyata dengan memanggil init
cukup dengan mengimplementasikan init, yang mengembalikan nihil dan memiliki inisialisasi (pribadi / tidak terlihat) yang nama orang lain tidak akan dapatkan (seperti initOnce, initWithSpecial ...)
Catatan: seseorang dapat melakukan ini
dan itu sebenarnya akan mengembalikan contoh baru, tetapi jika initOnce tidak akan diumumkan dalam proyek kami (di header), itu akan menghasilkan peringatan (id mungkin tidak merespons ...) dan lagi pula orang yang menggunakan ini, perlu untuk mengetahui dengan tepat bahwa inisialisasi sebenarnya adalah initOnce
kita bisa mencegah ini lebih jauh, tetapi tidak perlu
sumber
Saya harus menyebutkan bahwa menempatkan pernyataan dan meningkatkan pengecualian untuk menyembunyikan metode dalam subkelas memiliki jebakan buruk untuk yang dimaksudkan dengan baik.
Saya akan merekomendasikan penggunaan
__unavailable
seperti yang dijelaskan Jano untuk contoh pertamanya .Metode dapat diganti dalam subkelas. Ini berarti bahwa jika suatu metode dalam superclass menggunakan metode yang hanya menimbulkan pengecualian dalam subkelas, itu mungkin tidak akan berfungsi sebagaimana dimaksud. Dengan kata lain, Anda baru saja melanggar apa yang dulu bekerja. Ini berlaku dengan metode inisialisasi juga. Berikut adalah contoh implementasi yang agak umum:
Bayangkan apa yang terjadi pada -initWithLessParameters, jika saya melakukan ini di subkelas:
Ini menyiratkan bahwa Anda harus cenderung menggunakan metode pribadi (tersembunyi), terutama dalam metode inisialisasi, kecuali jika Anda berencana untuk mengganti metode tersebut. Tapi, ini adalah topik lain, karena Anda tidak selalu memiliki kendali penuh dalam implementasi superclass. (Ini membuat saya mempertanyakan penggunaan __attribute ((objc_designated_initializer)) sebagai praktik yang buruk, meskipun saya belum menggunakannya secara mendalam.)
Ini juga menyiratkan bahwa Anda dapat menggunakan pernyataan dan pengecualian dalam metode yang harus ditimpa dalam subkelas. (Metode "abstrak" seperti dalam Membuat kelas abstrak di Objective-C )
Dan, jangan lupakan + metode kelas baru.
sumber