Saya telah melihat sejumlah strategi untuk mendeklarasikan metode semi-privat di Objective-C , tetapi tampaknya tidak ada cara untuk membuat metode yang benar-benar privat. Saya menerima itu. Tetapi, mengapa demikian? Setiap penjelasan pada dasarnya saya katakan, "Anda tidak bisa melakukannya, tapi ini perkiraan yang mendekati."
Ada sejumlah kata kunci diterapkan ivars
(anggota) yang mengontrol ruang lingkup mereka, misalnya @private
, @public
, @protected
. Mengapa ini tidak bisa dilakukan untuk metode juga? Sepertinya sesuatu yang harus didukung oleh runtime. Apakah ada filosofi mendasar yang saya lewatkan? Apakah ini disengaja?
objective-c
objective-c-runtime
Rob Jones
sumber
sumber
Jawaban:
Jawabannya adalah ... baik ... sederhana. Kesederhanaan dan konsistensi sebenarnya.
Objective-C murni dinamis pada saat pengiriman metode. Secara khusus, setiap pengiriman metode melewati titik resolusi metode dinamis yang sama persis seperti setiap pengiriman metode lainnya. Saat runtime, setiap implementasi metode memiliki eksposur yang sama persis dan semua API yang disediakan oleh runtime Objective-C yang bekerja dengan metode dan penyeleksi bekerja sama di semua metode.
Seperti yang telah dijawab banyak orang (baik di sini dan di pertanyaan lain), metode privat waktu kompilasi didukung; jika sebuah kelas tidak mendeklarasikan metode dalam antarmuka yang tersedia untuk umum, maka metode itu mungkin juga tidak ada sejauh menyangkut kode Anda. Dengan kata lain, Anda dapat mencapai semua dari berbagai kombinasi visibilitas yang diinginkan pada waktu kompilasi dengan mengatur proyek Anda secara tepat.
Ada sedikit manfaat untuk menduplikasi fungsionalitas yang sama ke dalam runtime. Ini akan menambah kerumitan dan overhead yang luar biasa. Dan bahkan dengan semua kerumitan itu, itu tetap tidak akan mencegah semua kecuali pengembang yang paling biasa untuk menjalankan metode yang Anda anggap "pribadi".
Namun, melakukan hal itu akan merusak sifat dinamis murni bahasa tersebut. Setiap metode pengiriman tidak lagi melalui mekanisme pengiriman yang identik. Sebaliknya, Anda akan dibiarkan dalam situasi di mana sebagian besar metode berperilaku satu arah dan segelintir kecil hanya berbeda.
Ini melampaui runtime karena ada banyak mekanisme di Cocoa yang dibangun di atas dinamisme Objective-C yang konsisten. Misalnya, Pengkodean Nilai Kunci dan Pengamatan Nilai Kunci harus dimodifikasi secara besar-besaran untuk mendukung metode privat - kemungkinan besar dengan membuat celah yang dapat dieksploitasi - atau metode privat tidak akan kompatibel.
sumber
Runtime dapat mendukungnya tetapi biayanya akan sangat besar. Setiap selektor yang dikirim perlu diperiksa apakah privat atau publik untuk kelas itu, atau setiap kelas perlu mengelola dua tabel pengiriman terpisah. Ini tidak sama untuk variabel instance karena tingkat perlindungan ini dilakukan pada waktu kompilasi.
Selain itu, runtime perlu memverifikasi bahwa pengirim pesan pribadi memiliki kelas yang sama dengan penerima. Anda juga bisa melewati metode privat; jika kelas digunakan
instanceMethodForSelector:
, itu bisa memberikan keuntunganIMP
ke kelas lain bagi mereka untuk memanggil metode privat secara langsung.Metode pribadi tidak dapat melewati pengiriman pesan. Pertimbangkan skenario berikut ini:
Kelas
AllPublic
memiliki metode instance publikdoSomething
Kelas lain
HasPrivate
memiliki metode instance pribadi yang juga disebutdoSomething
Anda membuat array yang berisi sejumlah instance dari
AllPublic
danHasPrivate
Anda memiliki loop berikut:
Jika Anda berlari loop yang dari dalam
AllPublic
, runtime harus menghentikan Anda mengirimdoSomething
padaHasPrivate
contoh, namun lingkaran ini akan dapat digunakan jika itu di dalamHasPrivate
kelas.sumber
Jawaban yang diposting sejauh ini berfungsi dengan baik dalam menjawab pertanyaan dari perspektif filosofis, jadi saya akan mengajukan alasan yang lebih pragmatis: apa yang akan diperoleh dengan mengubah semantik bahasa? Cukup sederhana untuk "menyembunyikan" metode privat secara efektif. Sebagai contoh, bayangkan Anda memiliki kelas yang dideklarasikan dalam file header, seperti:
Jika Anda membutuhkan metode "pribadi", Anda juga dapat memasukkannya ke dalam file implementasi:
Tentu, ini tidak persis sama dengan metode pribadi C ++ / Java, tetapi cukup dekat secara efektif, jadi mengapa mengubah semantik bahasa, serta kompilator, runtime, dll., Untuk menambahkan fitur yang sudah diemulasi dalam bentuk yang dapat diterima. cara? Seperti dicatat dalam jawaban lain, semantik penyampaian pesan - dan ketergantungannya pada refleksi waktu proses - akan membuat penanganan pesan "pribadi" menjadi tidak sepele.
sumber
Solusi termudah adalah dengan mendeklarasikan beberapa fungsi C statis di kelas Objective-C Anda. Ini hanya memiliki ruang lingkup file sesuai aturan C untuk kata kunci statis dan karena itu mereka hanya dapat digunakan oleh metode di kelas itu.
Tidak ada masalah sama sekali.
sumber
Ya, ini bisa dilakukan tanpa mempengaruhi runtime dengan menggunakan teknik yang sudah digunakan oleh compiler untuk menangani C ++: name-mangling.
Ini belum dilakukan karena belum ditetapkan bahwa itu akan menyelesaikan beberapa kesulitan yang cukup besar dalam ruang masalah pengkodean yang teknik lain (misalnya, memberi awalan atau garis bawah) mampu mengelak secara memadai. IOW, Anda membutuhkan lebih banyak rasa sakit untuk mengatasi kebiasaan yang sudah mendarah daging.
Anda dapat menyumbangkan tambalan ke clang atau gcc yang menambahkan metode privat ke sintaks dan menghasilkan nama-nama rusak yang dikenali sendiri selama kompilasi (dan segera dilupakan). Kemudian orang lain di komunitas Objective-C akan dapat menentukan apakah itu benar-benar bermanfaat atau tidak. Kemungkinan akan lebih cepat seperti itu daripada mencoba meyakinkan para pengembang.
sumber
Pada dasarnya, ini ada hubungannya dengan bentuk panggilan metode penyampaian pesan Objective-C. Pesan apa pun dapat dikirim ke objek apa pun, dan objek tersebut memilih cara merespons pesan tersebut. Biasanya ia akan merespons dengan menjalankan metode yang dinamai menurut pesan, tetapi ia juga dapat merespons dengan beberapa cara lain. Ini tidak membuat metode privat sama sekali tidak mungkin - Ruby melakukannya dengan sistem penyampaian pesan yang serupa - tetapi itu membuatnya agak canggung.
Bahkan penerapan metode privat Ruby agak membingungkan orang karena keanehannya (Anda dapat mengirim objek pesan apa pun yang Anda suka, kecuali yang ada di daftar ini !). Pada dasarnya, Ruby membuatnya bekerja dengan melarang metode privat untuk dipanggil dengan penerima eksplisit. Di Objective-C itu akan membutuhkan lebih banyak pekerjaan karena Objective-C tidak memiliki opsi itu.
sumber
Ini masalah dengan lingkungan runtime Objective-C. Saat C / C ++ dikompilasi menjadi kode mesin yang tidak dapat dibaca, Objective-C masih mempertahankan beberapa atribut yang dapat dibaca manusia seperti nama metode sebagai string . Ini memberi Objective-C kemampuan untuk melakukan fitur reflektif .
EDIT: Menjadi bahasa reflektif tanpa metode privat yang ketat membuat Objective-C lebih "pythonic" karena Anda mempercayai orang lain yang menggunakan kode Anda daripada membatasi metode apa yang dapat mereka panggil. Menggunakan konvensi penamaan seperti garis bawah ganda dimaksudkan untuk menyembunyikan kode Anda dari pembuat kode klien biasa, tetapi tidak akan menghentikan pembuat kode perlu melakukan pekerjaan yang lebih serius.
sumber
Ada dua jawaban tergantung pada interpretasi pertanyaan.
Yang pertama adalah dengan menyembunyikan implementasi metode dari antarmuka. Ini digunakan, biasanya dengan kategori tanpa nama (mis
@interface Foo()
). Ini mengizinkan objek untuk mengirim pesan-pesan itu tetapi tidak yang lain - meskipun seseorang mungkin masih menimpa secara tidak sengaja (atau sebaliknya).Jawaban kedua, dengan asumsi bahwa ini tentang kinerja dan inlining, dimungkinkan tetapi sebagai fungsi C lokal. Jika Anda menginginkan metode 'private foo (
NSString *arg
)', Anda akan melakukanvoid MyClass_foo(MyClass *self, NSString *arg)
dan menyebutnya sebagai fungsi C.MyClass_foo(self,arg)
. Sintaksnya berbeda, tetapi bekerja dengan karakteristik kinerja yang wajar dari metode privat C ++.Meskipun ini menjawab pertanyaan, saya harus menunjukkan bahwa kategori tanpa nama sejauh ini merupakan cara Objective-C yang lebih umum untuk melakukan ini.
sumber
Objective-C tidak mendukung metode privat karena tidak membutuhkannya.
Dalam C ++, setiap metode harus terlihat dalam deklarasi kelas. Anda tidak dapat memiliki metode yang tidak dapat dilihat oleh seseorang termasuk file header. Jadi jika Anda menginginkan metode yang kode di luar implementasi Anda tidak boleh digunakan, Anda tidak punya pilihan, kompilator harus memberi Anda beberapa alat sehingga Anda dapat mengatakan bahwa metode tersebut tidak boleh digunakan, yaitu kata kunci "pribadi".
Di Objective-C, Anda bisa memiliki metode yang tidak ada di file header. Jadi, Anda mencapai tujuan yang sama dengan sangat mudah dengan tidak menambahkan metode ke file header. Tidak perlu metode pribadi. Objective-C juga memiliki keuntungan bahwa Anda tidak perlu mengkompilasi ulang setiap pengguna kelas karena Anda mengubah metode privat.
Misalnya variabel, yang sebelumnya harus Anda deklarasikan di file header (tidak lagi), @private, @public dan @protected tersedia.
sumber
Jawaban yang hilang di sini adalah: karena metode privat adalah ide yang buruk dari sudut pandang kemampuan berkembang. Tampaknya ide yang bagus untuk membuat metode menjadi pribadi saat menulisnya, tetapi ini adalah bentuk pengikatan awal. Konteksnya mungkin berubah, dan pengguna selanjutnya mungkin ingin menggunakan implementasi yang berbeda. Sedikit provokatif: "Pengembang yang gesit tidak menggunakan metode pribadi"
Di satu sisi, seperti Smalltalk, Objective-C ditujukan untuk programmer dewasa. Kami menghargai mengetahui apa yang diasumsikan pengembang asli sebagai antarmuka, dan bertanggung jawab untuk menangani konsekuensinya jika kami perlu mengubah implementasi. Jadi ya, ini filosofi, bukan implementasi.
sumber