Mengapa Objective-C tidak mendukung metode privat?

123

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?

Rob Jones
sumber
15
Pertanyaan bagus, btw.
bbum
5
Untuk hasil edit Anda: ya, penerapan waktu proses akan sangat mahal untuk keuntungan yang sangat kecil. Penegakan waktu kompilasi akan menjadi tambahan yang disambut baik untuk bahasa ini dan cukup bisa dicapai. Ya, itu bisa dielakkan saat runtime, tapi tidak apa-apa. Programmer bukanlah musuh. Tujuan dari ruang lingkup adalah untuk membantu melindungi programmer dari kesalahan.
Rob Napier
1
Anda tidak dapat "melewatkan runtime" - runtime itulah yang mengirimkan pesan.
Chuck
"Tidak akan ada cara bagi metode pribadi untuk memutus arus listrik cek ini" Apakah maksud Anda "mengelak"? ;-)
Constantino Tsarouhas

Jawaban:

103

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".

EDIT: Salah satu asumsi yang saya perhatikan adalah bahwa pesan pribadi harus melalui waktu proses sehingga berpotensi menimbulkan overhead besar. Apakah ini benar?

Ya itu. Tidak ada alasan untuk menganggap bahwa implementor kelas tidak ingin menggunakan semua set fitur Objective-C dalam implementasi, dan itu berarti pengiriman dinamis harus terjadi. Namun , tidak ada alasan khusus mengapa metode privat tidak dapat dikirim oleh varian khusus objc_msgSend(), karena kompilator akan tahu bahwa metode privat; yaitu hal ini dapat dicapai dengan menambahkan tabel metode khusus pribadi ke Classstruktur.

Tidak akan ada cara bagi metode pribadi untuk melakukan hubungan pendek pada pemeriksaan ini atau melewati runtime?

Itu tidak bisa melewati waktu proses, tetapi waktu proses tidak perlu melakukan pemeriksaan apa pun untuk metode pribadi.

Meskipun demikian, tidak ada alasan bahwa pihak ketiga tidak dapat dengan sengaja memanggil objc_msgSendPrivate()suatu objek, di luar implementasi objek tersebut, dan beberapa hal (KVO, misalnya) harus melakukan itu. Akibatnya, ini hanya akan menjadi konvensi dan sedikit lebih baik dalam praktiknya daripada mengawali pemilih metode pribadi atau tidak menyebutkannya di header antarmuka.

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.

bbum
sumber
49
+1 Objective-C adalah (dalam beberapa hal) bahasa "anak laki-laki besar" di mana pemrogram diharapkan untuk menunjukkan sejumlah disiplin, daripada ditampar oleh kompiler / runtime di setiap kesempatan. Saya merasa itu menyegarkan. Bagi saya, membatasi visibilitas metode selama kompilasi / penautan sudah cukup dan lebih disukai.
Quinn Taylor
11
Lebih banyak python menjadi "obj-c-ic" :). Guido cukup proaktif dalam mempertahankan Python di sistem NeXT, termasuk membuat PyObjC versi pertama. Dengan demikian, ObjC melakukan pengaruh python agak .
bbum
3
1 untuk kisah bersejarah yang menarik tentang diktator yang baik hati seumur hidup dan persilangannya dengan sistem mitos NeXT.
pokstad
5
Terima kasih. Python, Java dan Objective-C semuanya memiliki model objek runtime yang sangat mirip [dengan SmallTalk]. Java secara langsung diturunkan dari Objective-C. Python menjalankan kursus paralel lebih dari satu inspirasi, tetapi Guido tentu saja mempelajari NeXTSTEP dengan cermat.
bbum
1
Kecuali saya tidak mempercayai lisensinya dan saya pasti lebih suka dentang ke gcc. Tapi itu langkah pertama yang pasti.
Cthutu
18

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:

  1. Kelas AllPublicmemiliki metode instance publikdoSomething

  2. Kelas lain HasPrivatememiliki metode instance pribadi yang juga disebutdoSomething

  3. Anda membuat array yang berisi sejumlah instance dari AllPublicdanHasPrivate

  4. Anda memiliki loop berikut:

    for (id anObject in myArray)
        [anObject doSomething];

    Jika Anda berlari loop yang dari dalam AllPublic, runtime harus menghentikan Anda mengirim doSomethingpada HasPrivatecontoh, namun lingkaran ini akan dapat digunakan jika itu di dalam HasPrivatekelas.

dreamlax
sumber
1
Saya setuju bahwa akan ada biaya yang harus dikeluarkan. Mungkin itu bukan sesuatu yang Anda inginkan pada perangkat genggam. Bisakah Anda mendukung kualifikasi yang sangat besar? Apakah biaya benar-benar menjadi masalah pada sistem desktop?
Rob Jones
1
Belum pernah melakukannya, tetapi Anda mungkin benar. Objective-C memiliki pengiriman pesan waktu proses vs. panggilan metode waktu kompilasi dari C ++, jadi Anda akan berbicara tentang pemeriksaan waktu kompilasi vs. pemeriksaan waktu proses.
Remus Rusanu
Tampaknya sangat aneh bahwa alasan utama tidak mendukung metode privat adalah karena kinerja. Saya pikir Apple sama sekali tidak menganggap metode pribadi sangat diperlukan, apakah ada kinerja yang sukses atau tidak. Jika tidak, mereka seharusnya memilih bahasa yang berbeda untuk perusahaan multi-miliar dolar mereka.
pokstad
1
Menambahkan metode pribadi tidak hanya akan mempersulit implementasi runtime, tetapi juga akan memperumit semantik bahasa. Kontrol akses adalah konsep sederhana dalam bahasa statis, tetapi tidak cocok untuk pengiriman dinamis yang akan datang dengan banyak overhead konseptual dan mengarah ke banyak, banyak “mengapa ini tidak berhasil?” pertanyaan.
Jens Ayton
7
Apa metode pribadi akan membelikan Anda? Objective-C, bagaimanapun, adalah bahasa berbasis C. Jadi, jika seseorang memiliki kode yang berjalan dalam proses Anda, mereka dapat dengan mudah melakukan p0wnz proses Anda terlepas dari seberapa pribadi atau disamarkannya API apa pun.
bbum
13

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:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

Jika Anda membutuhkan metode "pribadi", Anda juga dapat memasukkannya ke dalam file implementasi:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

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.

mipadi
sumber
1
Masalah dengan pendekatan ini adalah bahwa seseorang dapat mengganti metode privat (bahkan tanpa disadari) saat mereka membuat subkelas kelas Anda. Saya kira, pada dasarnya Anda harus memilih nama yang tidak mungkin diganti.
dreamlax
3
Yang terasa seperti retasan di atas retasan bagi saya.
Rob Jones
1
@Mike: Apple telah menyatakan bahwa nama metode dengan garis bawah di depan secara eksplisit hanya disediakan untuk Apple: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . Alternatif yang disarankan adalah memberi awalan dengan dua huruf kapital dan kemudian garis bawah.
dreamlax
8
Juga, masukkan SSN Anda setelah metode sehingga pemrogram lain tahu siapa yang menulisnya. = D Namespaces, membutuhkannya !!!
pokstad
2
Jika Anda khawatir tentang metode yang Anda definisikan diganti, Anda tidak benar-benar merangkul tingkat verbositas inheren yang didorong oleh Objective-C ...
Kendall Helmstetter Gelner
7

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.

Kromulen
sumber
Apakah ini harus ditentukan di luar bagian @implementation?
Rob Jones
@Rob - tidak, mereka dapat (dan kemungkinan besar harus) ditentukan dalam implementasi kelas Anda.
Cromulent
6

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.

Huperniketes
sumber
2
Mengompilasi nama waktu mangling jauh lebih sulit daripada yang mungkin diasumsikan karena Anda harus mengacaukan semua pemilih metode, termasuk yang mungkin muncul di file XIB dan yang mungkin diteruskan ke hal-hal seperti NSSelectorFromString () serta KeyValueCoding & teman ...
bbum
1
Ya, akan lebih terlibat jika Anda ingin memberikan dukungan untuk metode privat di seluruh rantai alat. Tabel simbol kompiler perlu disimpan, dan dapat diakses melalui beberapa api toolchain. Ini bukan pertama kalinya Apple harus secara bertahap meluncurkan kapabilitas kepada pengembang yang cukup penting karena waktu dan stabilitas diizinkan. Namun, jika menyangkut metode pribadi: patut dipertanyakan apakah itu kemenangan yang cukup untuk melakukan upaya tersebut. Bahkan lebih mencurigakan bahwa mereka harus dapat diakses di luar kelas itu sendiri dengan mekanisme lain ini.
Huperniketes
1
Bagaimana penegakan + pengubahan nama hanya waktu kompilasi menghentikan seseorang berjalan di daftar metode kelas dan menemukannya di sana?
dreamlax
Tidak akan. Jawaban saya hanya menjawab pertanyaan yang diajukan oleh OP.
Huperniketes
Saya telah berpikir untuk mencoba menambahkan metode privat ke Clang, salah satu alasan saya pikir metode privat akan baik adalah karena metode tersebut dapat dipanggil secara langsung seperti fungsi c sederhana dan kemudian Anda dapat memiliki semua pengoptimalan fungsi C yang biasa terjadi. Saya tidak pernah repot karena waktu dan Anda sudah bisa melakukan ini hanya dengan menggunakan c.
Hari Nathan
4

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.

Membuang
sumber
1

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.

pokstad
sumber
Dan jika konsumen lib Anda dapat mencerminkan metode pribadi Anda, tidak bisakah mereka memanggilnya juga?
Jared Updike
Jika mereka tahu ke mana mencarinya, bukankah itu cara orang menggunakan perpustakaan tidak berdokumen di iPhone? Masalah dengan iPhone adalah Anda berisiko tidak menerima aplikasi Anda untuk menggunakan API pribadi apa pun.
pokstad
Jika mereka mengakses metode privat melalui refleksi, mereka mendapatkan apa yang pantas mereka dapatkan. Setiap kesalahan bukanlah salah Anda.
gnasher729
1

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 melakukan void 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.

AlBlue
sumber
0

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.

gnasher729
sumber
0

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.

Stephan Eggermont
sumber
Saya telah memilih ini karena tidak pantas mendapatkan suara negatif. Ada, seperti yang dikatakan Stephan, argumen bahwa metode "pribadi" tidak diperlukan, dan ini mirip dengan argumen tentang pengetikan dinamis vs statis, di mana, apa pun kesimpulan yang Anda sukai, kedua belah pihak ada benarnya.
alastair