Saya punya dua kelas, Shape
danSquare
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Dengan implementasi di atas saya mendapatkan kesalahan:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Mengapa saya harus mengatur self.sideLength
sebelum menelepon super.init
?
properties
compiler-errors
swift
JuJoDi
sumber
sumber
Jawaban:
Kutipan dari Bahasa Pemrograman Swift, yang menjawab pertanyaan Anda:
sumber
init
.Swift memiliki urutan operasi yang sangat jelas dan spesifik yang dilakukan pada inisialisasi. Mari kita mulai dengan beberapa contoh dasar dan menyelesaikan kasus umum.
Mari kita ambil objek A. Kita akan mendefinisikannya sebagai berikut.
Perhatikan bahwa A tidak memiliki superclass, sehingga tidak dapat memanggil fungsi super.init () karena tidak ada.
OK, jadi sekarang mari kita subkelas A dengan kelas baru bernama B.
Ini adalah keberangkatan dari Objective-C di mana
[super init]
biasanya akan dipanggil dulu sebelum yang lainnya. Tidak demikian halnya dengan Swift. Anda bertanggung jawab untuk memastikan bahwa variabel instan Anda dalam keadaan konsisten sebelum Anda melakukan hal lain, termasuk metode pemanggilan (yang mencakup penginisialisasi superclass Anda).sumber
Dari dokumen
Mengapa kita perlu pemeriksaan keamanan seperti ini?
Untuk menjawab ini mari kita lanjutkan proses inisialisasi dalam cepat.
Jadi, untuk memastikan proses inisialisasi dua langkah dilakukan sebagaimana didefinisikan di atas, ada empat pemeriksaan keamanan, salah satunya adalah,
Sekarang, inisialisasi dua fase tidak pernah berbicara tentang pesanan, tetapi pemeriksaan keamanan ini, diperkenalkan
super.init
untuk dipesan, setelah inisialisasi semua properti.Pemeriksaan keamanan 1 mungkin tampak tidak relevan karena, inisialisasi dua fase mencegah nilai properti diakses sebelum diinisialisasi dapat dipenuhi, tanpa pemeriksaan keamanan ini 1.
Seperti dalam sampel ini
Triangle.init
telah diinisialisasi, setiap properti sebelum digunakan. Jadi pemeriksaan Keselamatan 1 tampaknya tidak relevan,Tapi kemudian mungkin ada skenario lain, sedikit rumit,
Output:
Di sini jika kita telah memanggil
super.init
pengaturan sebelumnyahypotenuse
,super.init
panggilan itu akan memanggilprintShapeDescription()
dan karena itu telah ditimpa akan pertama kali mundur ke implementasi kelas SegitigaprintShapeDescription()
. KelasprintShapeDescription()
Triangle mengakseshypotenuse
properti non-opsional yang masih belum diinisialisasi. Dan ini tidak diperbolehkan karena inisialisasi dua fase mencegah nilai properti diakses sebelum diinisialisasiJadi pastikan inisialisasi Dua fase dilakukan seperti yang didefinisikan, perlu ada urutan panggilan tertentu
super.init
, dan itu adalah, setelah menginisialisasi semua properti yang diperkenalkan olehself
kelas, maka kita perlu pemeriksaan Keselamatan 1sumber
init
dapat memanggil fungsi (overriden) ... di mana fungsi itu mengakses properti subclass, maka untuk menghindari tidak memiliki nilai yang ditetapkan, panggilan untuksuper
harus terjadi setelah semua nilai ditetapkan. OK masuk akal. Bertanya-tanya bagaimana Objective-C melakukannya saat itu dan mengapa Anda harus meneleponsuper
dulu?printShapeDescription()
sebelumself.sides = sides; self.name = named;
yang akan menghasilkan kesalahan ini:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. Kesalahan OP diberikan untuk mengurangi 'kemungkinan' kesalahan runtime.printShapeDescription
fungsi yang tidak merujukself
yaitu yaitu sesuatu seperti `cetak (" tidak ada ") maka tidak akan ada masalah. (Namun bahkan untuk itu kompiler akan membuat kesalahan, karena itu tidak super pintar)"Super.init ()" harus dipanggil setelah Anda menginisialisasi semua variabel instan Anda.
Dalam video "Intermediate Swift" Apple (Anda dapat menemukannya di halaman sumber video Pengembang Apple https://developer.apple.com/videos/wwdc/2014/ ), sekitar 28:40, secara eksplisit dikatakan bahwa semua inisialisasi di kelas super harus disebut SETELAH Anda menginisialisasi variabel instan Anda.
Di Objective-C, itu kebalikannya. Di Swift, karena semua properti harus diinisialisasi sebelum digunakan, kita perlu menginisialisasi properti terlebih dahulu. Ini dimaksudkan untuk mencegah panggilan ke fungsi utama dari metode "init ()" kelas super, tanpa menginisialisasi properti terlebih dahulu.
Jadi implementasi "Square" harus:
sumber
Maaf untuk pemformatan jelek. Cukup masukkan karakter pertanyaan setelah deklarasi dan semuanya akan beres. Sebuah pertanyaan memberi tahu kompiler bahwa nilainya opsional.
Sunting1:
Ada cara yang lebih baik untuk melewati kesalahan ini. Menurut komentar jmaschad, tidak ada alasan untuk menggunakan opsional dalam kasus Anda karena opsional tidak nyaman digunakan dan Anda selalu harus memeriksa apakah opsional tidak nol sebelum mengaksesnya. Jadi yang harus Anda lakukan adalah menginisialisasi anggota setelah deklarasi:
Sunting2:
Setelah dua minus mendapat jawaban ini saya menemukan cara yang lebih baik. Jika Anda ingin anggota kelas diinisialisasi dalam konstruktor Anda, Anda harus menetapkan nilai awal untuk itu di dalam konstruktor dan sebelum super.init () panggilan. Seperti ini:
Semoga beruntung dalam belajar Swift.
sumber
super.init(name:name)
danself.sideLength = sideLength
. MendeklarasikansideLength
sebagai opsional adalah salah arah dan memperkenalkan kerumitan tambahan nanti saat Anda harus memaksanya membuka bungkusannya.var sideLength: Double
, tidak perlu menetapkannya nilai awalswift memaksa Anda untuk menginisialisasi setiap anggota sebelum itu pernah / mungkin pernah digunakan. Karena tidak dapat memastikan apa yang terjadi ketika giliran makan malam berubah, itu kesalahan: lebih baik aman daripada menyesal
sumber
super.init
di baris pertama?Edward,
Anda dapat mengubah kode dalam contoh Anda seperti ini:
Ini menggunakan opsional yang terbuka secara implisit.
Dalam dokumentasi kita dapat membaca:
sumber
Swift tidak akan memungkinkan Anda untuk menginisialisasi kelas super tanpa menginisialisasi properti, kebalikan dari Obj C. Jadi Anda harus menginisialisasi semua properti sebelum memanggil "super.init".
Silakan buka http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Ini memberikan penjelasan yang bagus untuk masalah Anda.
sumber
Tambahkan nil ke akhir deklarasi.
Ini berfungsi untuk kasus saya, tetapi mungkin tidak bekerja untuk Anda
sumber
Anda hanya memulai dengan urutan yang salah.
sumber
Saya akan menerima beberapa downvotes mungkin, tetapi jujur, hidup lebih mudah dengan cara ini:
sumber
Ini adalah desain yang sangat bodoh.
Pertimbangkan sesuatu seperti ini:
Ini tidak valid, seperti disebutkan di atas. Tapi begitu juga:
Karena 'diri' belum diinisialisasi.
Saya sangat berharap bug ini segera diperbaiki.
(Ya saya tahu saya bisa membuat objek kosong dan kemudian mengatur ukuran tapi itu hanya bodoh).
sumber