Saya punya pertanyaan umum tentang menulis metode init di Objective-C.
Saya melihatnya di mana-mana (kode Apple, buku, kode sumber terbuka, dll.) Bahwa metode init harus memeriksa apakah self = [super init] tidak nol sebelum melanjutkan dengan inisialisasi.
Template Apple default untuk metode init adalah:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
Mengapa?
Maksud saya kapan init akan kembali nihil? Jika saya memanggil init di NSObject dan mendapat nil kembali, maka sesuatu pasti benar-benar kacau, bukan? Dan dalam hal ini, Anda mungkin bahkan tidak menulis program ...
Apakah benar-benar umum bahwa metode init kelas dapat mengembalikan nol? Jika demikian, dalam hal apa, dan mengapa?
objective-c
null
init
Jasarien
sumber
sumber
Jawaban:
Sebagai contoh:
... dan seterusnya. (Catatan: NSData mungkin melempar pengecualian jika file tidak ada). Ada beberapa area di mana pengembalian nol adalah perilaku yang diharapkan ketika masalah terjadi, dan karena ini merupakan praktik standar untuk memeriksa nol hampir sepanjang waktu, demi konsistensi.
sumber
Ungkapan khusus ini adalah standar karena ia berfungsi dalam semua kasus.
Meskipun tidak umum, akan ada kasus di mana ...
... mengembalikan contoh yang berbeda, sehingga membutuhkan penugasan untuk diri sendiri.
Dan akan ada kasus-kasus di mana ia akan mengembalikan nil, sehingga membutuhkan pemeriksaan nil sehingga kode Anda tidak mencoba untuk menginisialisasi slot variabel instan yang tidak ada lagi.
Intinya adalah bahwa itu adalah pola yang benar yang didokumentasikan untuk digunakan dan, jika Anda tidak menggunakannya, Anda salah melakukannya.
sumber
-init
efeknya ...)[super init]
mengembalikan nol ketika superclass langsungNSObject
? Bukankah ini kasus di mana "semuanya rusak?"NSObject
adalah superclass langsung. Tapi ... bahkan jika Anda mendeklarasikanNSObject
sebagai superclass langsung, sesuatu bisa saja telah dimodifikasi pada saat runtime sehinggaNSObject
implementasiinit
bukan apa yang sebenarnya disebut.Saya pikir, di sebagian besar kelas, jika nilai pengembalian dari [super init] adalah nihil dan Anda memeriksanya, seperti yang disarankan oleh praktik standar, dan kemudian kembali secara prematur jika nihil, pada dasarnya aplikasi Anda masih tidak akan berfungsi dengan benar. Jika Anda berpikir tentang hal itu, meskipun jika (self! = Nil) periksa ada, untuk operasi yang tepat dari kelas Anda, 99,99% dari waktu Anda benar - benar membutuhkan diri untuk menjadi nihil. Sekarang, misalkan, untuk alasan apa pun, [yang super init] lakukan kembali nihil, pada dasarnya cek Anda terhadap nihil pada dasarnya lewat uang hingga pemanggil kelas Anda, di mana ia kemungkinan akan gagal lagian, karena secara alami akan menganggap bahwa panggilan itu berhasil
Pada dasarnya, yang saya maksudkan adalah 99,99% dari waktu, if (self! = Nil) tidak membelikan Anda apa pun dalam hal ketahanan yang lebih besar, karena Anda hanya menyerahkan uang kepada penyerang Anda. Untuk benar-benar dapat menangani ini dengan kuat, Anda benar-benar perlu melakukan cek di seluruh hierarki panggilan Anda. Dan meskipun begitu, satu-satunya hal yang akan membeli Anda adalah aplikasi Anda akan gagal sedikit lebih bersih / kuat. Tetapi itu masih akan gagal.
Jika kelas perpustakaan secara sewenang-wenang memutuskan untuk mengembalikan nol sebagai akibat dari [super init], Anda cukup banyak, dan itu lebih merupakan indikasi bahwa penulis kelas perpustakaan membuat kesalahan implementasi.
Saya pikir ini lebih merupakan saran pengkodean lama, ketika aplikasi berjalan dalam memori yang jauh lebih terbatas.
Tetapi untuk kode level C, saya biasanya masih akan memeriksa nilai pengembalian malloc () terhadap sebuah pointer NULL. Sedangkan, untuk Objective-C, sampai saya menemukan bukti yang bertentangan, saya pikir saya biasanya akan melewatkan pemeriksaan if (self! = Nil). Mengapa ada perbedaan?
Karena, pada level C dan malloc, dalam beberapa kasus Anda benar-benar dapat pulih sebagian. Sedangkan saya pikir dalam Objective-C, dalam 99,99% kasus, jika [super init] mengembalikan nol, Anda pada dasarnya kacau, bahkan jika Anda mencoba menanganinya. Anda mungkin juga membiarkan aplikasi crash dan berurusan dengan akibatnya.
sumber
Ini adalah semacam ringkasan komentar di atas.
Katakanlah superclass kembali
nil
. Apa yang akan terjadiJika Anda tidak mengikuti konvensi
Kode Anda akan macet di tengah
init
metode Anda . (Kecuali jikainit
tidak ada yang penting)Jika Anda mengikuti konvensi, tidak mengetahui bahwa superclass mungkin mengembalikan nol (kebanyakan orang berakhir di sini)
Kode Anda mungkin akan macet di beberapa titik nanti, karena instance Anda adalah
nil
, di mana Anda mengharapkan sesuatu yang berbeda. Atau program Anda akan berperilaku tak terduga tanpa menabrak. Oh sayang! Apakah anda menginginkan ini? Saya tidak tahu ...Jika Anda mengikuti konvensi, rela mengizinkan subclass Anda untuk mengembalikan nol
Dokumentasi kode Anda (!) Harus dengan jelas menyatakan: "mengembalikan ... atau nihil", dan sisa kode Anda perlu dipersiapkan untuk menangani ini. Sekarang masuk akal.
sumber
Biasanya, jika kelas Anda berasal langsung dari
NSObject
, Anda tidak perlu melakukannya. Namun, kebiasaan yang baik untuk masuk, seolah-olah kelas Anda berasal dari kelas lain, inisialisasi mereka dapat kembalinil
, dan jika demikian, inisialisasi Anda dapat menangkap itu dan bertindak dengan benar.Dan ya, sebagai catatan, saya mengikuti praktik terbaik dan menulisnya di semua kelas saya, bahkan yang berasal langsung
NSObject
.sumber
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
tidak menjamin itu-init
memberi AndaNSObject
juga, jika menghitung runtimes eksotis seperti versi GNUstep lama (yang mengembalikanGSObject
) jadi tidak peduli apa, cek dan tugas.Anda benar, Anda bisa sering hanya menulis
[super init]
, tetapi itu tidak akan berhasil untuk subkelas apa pun. Orang lebih suka hanya menghafal satu baris kode standar dan menggunakannya sepanjang waktu, bahkan ketika itu hanya kadang-kadang diperlukan, dan dengan demikian kita mendapatkan standarif (self = [super init])
, yang mengambil baik kemungkinan nihil dikembalikan dan kemungkinan objek selainself
dikembalikan memperhitungkan.sumber
Kesalahan umum adalah menulis
yang mengembalikan instance dari superclass, yang BUKAN apa yang Anda inginkan dalam konstruktor / init subclass. Anda mendapatkan kembali objek yang tidak menanggapi metode subkelas, yang dapat membingungkan, dan menghasilkan kesalahan membingungkan tentang tidak menanggapi metode atau pengidentifikasi yang tidak ditemukan, dll.
diperlukan jika kelas super memiliki anggota (variabel atau objek lain) untuk menginisialisasi terlebih dahulu sebelum mengatur anggota subclass. Kalau tidak, runtime objc menginisialisasi mereka semua ke 0 atau ke nol . ( tidak seperti ANSI C, yang sering mengalokasikan potongan memori tanpa membersihkannya sama sekali )
Dan ya, inisialisasi kelas dasar dapat gagal karena kesalahan kehabisan memori, komponen yang hilang, kegagalan perolehan sumber daya, dll. Jadi pemeriksaan untuk nil adalah bijaksana, dan membutuhkan waktu kurang dari beberapa milidetik.
sumber
Ini untuk memeriksa apakah intialazation bekerja, pernyataan if mengembalikan true jika metode init tidak mengembalikan nil, jadi itu cara untuk memeriksa pembuatan objek bekerja dengan benar. Beberapa alasan saya bisa memikirkan bahwa init mungkin gagal mungkin metode init yang ditimpa bahwa kelas super tidak tahu atau semacamnya, saya tidak akan berpikir itu adalah yang biasa sekalipun. Tapi jika itu terjadi, lebih baik tidak ada yang terjadi kalau saya kira begitu selalu diperiksa ...
sumber
Pada OS X, tidak mungkin
-[NSObject init]
gagal karena alasan memori. Hal yang sama tidak dapat dikatakan untuk iOS.Juga, praktik yang baik untuk menulis ketika mensubclass sebuah kelas yang mungkin kembali
nil
karena alasan apa pun.sumber
-[NSObject init]
adalah sangat tidak mungkin gagal karena alasan memori karena tidak mengalokasikan memori apapun.