Pertimbangkan dua kelas:
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
Saya tidak mengerti mengapa ini tidak diizinkan. Pada akhirnya, initializer ditunjuk masing-masing kelas ini disebut dengan nilai-nilai yang mereka butuhkan, jadi mengapa saya harus mengulang sendiri di B
's init
dengan menentukan nilai default untuk x
lagi, ketika kenyamanan init
di A
akan baik-baik saja?
class
initialization
swift
Robert
sumber
sumber
Jawaban:
Ini adalah Aturan 1 dari aturan "Penginisialisasi Chaining" seperti yang ditentukan dalam Panduan Pemrograman Swift, yang berbunyi:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Penekanan milikku. Penginisialisasi yang ditunjuk tidak dapat memanggil penginisialisasi praktis.
Ada diagram yang sesuai dengan aturan untuk menunjukkan "petunjuk" penginisialisasi apa yang diizinkan:
sumber
SCNGeometry
: Anda dapat menambahkanSCNGeometryElement
hanya dengan menggunakan penginisialisasi praktis, oleh karena itu seseorang tidak dapat mewarisinya.Mempertimbangkan
class A { var a: Int var b: Int init (a: Int, b: Int) { print("Entering A.init(a,b)") self.a = a; self.b = b } convenience init(a: Int) { print("Entering A.init(a)") self.init(a: a, b: 0) } convenience init() { print("Entering A.init()") self.init(a:0) } } class B : A { var c: Int override init(a: Int, b: Int) { print("Entering B.init(a,b)") self.c = 0; super.init(a: a, b: b) } } var b = B()
Karena semua penginisialisasi kelas A yang ditunjuk diganti, kelas B akan mewarisi semua penginisialisasi praktis dari A. Jadi mengeksekusi ini akan menampilkan
Entering A.init() Entering A.init(a:) Entering B.init(a:,b:) Entering A.init(a:,b:)
Sekarang, jika penginisialisasi B.init yang ditunjuk (a: b :) akan diizinkan untuk memanggil penginisialisasi kenyamanan kelas dasar A.init (a :), ini akan menghasilkan panggilan rekursif ke B.init (a:, b: ).
sumber
Itu karena Anda bisa berakhir dengan rekursi tak terbatas. Mempertimbangkan:
class SuperClass { init() { } convenience init(value: Int) { // calls init() of the current class // so init() for SubClass if the instance // is a SubClass self.init() } } class SubClass : SuperClass { override init() { super.init(value: 10) } }
dan lihat:
let a = SubClass()
yang akan memanggil
SubClass.init()
yang akan memanggilSuperClass.init(value:)
yang akan memanggilSubClass.init()
.Aturan init yang ditunjuk / praktis dirancang agar inisialisasi kelas akan selalu benar.
sumber
Saya menemukan solusi untuk ini. Ini tidak super cantik, tetapi memecahkan masalah tidak mengetahui nilai superclass atau ingin menyetel nilai default.
Yang harus Anda lakukan adalah membuat instance dari superclass, menggunakan kemudahan
init
, tepat diinit
subclass. Kemudian Anda memanggil yang ditunjukinit
dari super menggunakan contoh yang baru saja Anda buat.class A { var x: Int init(x: Int) { self.x = x } convenience init() { self.init(x: 0) } } class B: A { init() { // calls A's convenience init, gets instance of A with default x value let intermediate = A() super.init(x: intermediate.x) } }
sumber
Pertimbangkan untuk mengekstrak kode inisialisasi dari kemudahan Anda
init()
ke fungsi pembantu barufoo()
, panggilfoo(...)
untuk melakukan inisialisasi di sub-kelas Anda.sumber
Lihat video WWDC "403 intermediate Swift" di 18:30 untuk penjelasan mendalam tentang penginisialisasi dan warisannya. Seperti yang saya pahami, pertimbangkan hal berikut:
class Dragon { var legs: Int var isFlying: Bool init(legs: Int, isFlying: Bool) { self.legs = legs self.isFlying = isFlying } convenience initWyvern() { self.init(legs: 2, isFlying: true) } }
Tapi sekarang pertimbangkan subkelas Wyrm: Wyrm adalah Naga tanpa kaki dan sayap. Jadi Penginisialisasi untuk Wyvern (2 kaki, 2 sayap) salah untuk itu! Kesalahan itu dapat dihindari jika kenyamanan Wyvern-Initializer tidak dapat dipanggil tetapi hanya Penginisialisasi lengkap yang ditunjuk:
class Wyrm: Dragon { init() { super.init(legs: 0, isFlying: false) } }
sumber
initWyvern
masuk akal untuk dipanggil?Wyrm
menimpa jumlah kaki setelah memanggil penginisialisasi kenyamanan.Mengapa Anda tidak hanya memiliki dua penginisialisasi - satu dengan nilai default?
class A { var x: Int init(x: Int) { self.x = x } init() { self.x = 0 } } class B: A { override init() { super.init() // Do something else } } let s = B() s.x // 0
sumber