Apa sebenarnya init coder aDecoder?

122

Saya mempelajari pengembangan iOS dari kursus online dan setiap kali saya membuat tampilan kustom (sel tampilan tabel kustom, sel tampilan koleksi, dll.) Instruktur selalu menerapkan penginisialisasi ini:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Mengapa saya harus selalu menyebutnya demikian? Apa fungsinya? Bisakah saya meletakkan properti di dalam init?

JasonP
sumber
5
Jawaban ini akan membantu Anda stackoverflow.com/questions/24036393/… Terima kasih
Seungyoun Yi
2
Jika Anda membuat subclass sebuah objek yang mengimplementasikan NSCodingmaka Anda perlu mengimplementasikan initialiser ini, karena ini diperlukan untuk class yang mengimplementasikan NSCoding. Anda setidaknya harus memanggil metode init superclass. Jika NSCoderberisi properti yang dikodekan untuk kelas Anda maka Anda dapat menggunakan metode ini untuk memulihkannya
Paulw11
1
Juga, saya sarankan Anda membaca bagian tentang inisialisasi objek di buku Swift resmi oleh Apple.
Nicolas Miari

Jawaban:

121

Saya akan memulai jawaban ini dari arah yang berlawanan: bagaimana jika Anda ingin menyimpan status tampilan Anda ke disk? Ini dikenal sebagai serialisasi . Kebalikannya adalah deserialization - memulihkan status objek dari disk.

The NSCodingprotokol mendefinisikan dua metode untuk cerita bersambung dan deserialize objek:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Jadi mengapa itu dibutuhkan di kelas khusus Anda? Jawabannya adalah Interface Builder. Saat Anda menyeret objek ke storyboard dan mengonfigurasinya, Pembuat Antarmuka membuat serial status objek tersebut ke disk, lalu deserialisasi saat storyboard muncul di layar. Anda perlu memberi tahu Pembuat Antarmuka cara melakukannya. Paling tidak, jika Anda tidak menambahkan properti baru apa pun ke subkelas Anda, Anda cukup meminta superclass untuk melakukan pengepakan dan pembongkaran untuk Anda, karenanya super.init(coder: aDecoder)dipanggil. Jika subclass Anda lebih kompleks, Anda perlu menambahkan kode serialisasi dan deserialisasi Anda sendiri untuk subclass tersebut.

Ini berbeda dengan pendekatan Visual Studio, yaitu menulis kode ke dalam file tersembunyi untuk membuat objek pada waktu proses.

Kode Berbeda
sumber
Mengapa tidak memasukkan semuanya ke dalam awakeFromNib dan lupa menggunakan init(coder aCoder : NSCoder)?
Madu
@ Madu - singkatnya, "terkadang Anda tidak bisa melakukan itu". Anda biasanya bisa tetapi tidak selalu.
Fattie
@Fattie adalah detail tentang tidak melakukannya terlalu rumit atau tidak perlu diketahui? Jika tidak keberatan menjelaskan?
Madu
9
@Honey jika Anda ingin mengkonfigurasi objek Anda di Interface Builder maka awakeFromNibtidak akan berfungsi. awakeFromNibdipanggil pada waktu proses . Apa pun yang Anda lakukan di Interface Builder adalah selama waktu desain . Untuk melaksanakan apa yang telah Anda lakukan dalam waktu desain untuk menjalankan waktu adalah encodeWithCoder(menghemat) dan init(coder:)(memuat)
Kode Berbeda
3
@Honey jika Anda tidak menggunakan Pembuat Antarmuka untuk mengonfigurasi kelas khusus Anda (yaitu melakukannya secara terprogram dengan kode) maka Anda dapat melakukannya di awakeFromNibatauinitWIthFrame
Kode Berbeda
28

Persyaratan untuk mengimplementasikan penginisialisasi tersebut merupakan konsekuensi dari dua hal:

  1. The Liskov prinsip substitusi . Jika S adalah subkelas dari T (misalnya MyViewControlleradalah subkelas dari ViewController), maka objek S (contoh dari MyViewController) harus dapat diganti di mana objek T (contoh dari ViewController) diharapkan.

  2. Penginisialisasi tidak diwarisi di Swift jika penginisialisasi apa pun secara eksplisit ditentukan dalam subkelas. Jika satu penginisialisasi disediakan secara eksplisit, maka semua penginisialisasi lainnya harus disediakan secara eksplisit (yang kemudian dapat dipanggil super.init(...)). Lihat pertanyaan ini untuk alasannya. Ada di Jawa, tapi tetap berlaku.

Pada poin 1, semua yang asli ViewControllerdapat lakukan, MyViewControllersubkelas harus dapat melakukannya. Salah satunya adalah dapat diinisialisasi dari yang diberikan NSCoder. Pada poin 2, MyViewControllersubkelas Anda tidak secara otomatis mewarisi kemampuan ini. Jadi, Anda harus menyediakan penginisialisasi yang memenuhi persyaratan ini secara manual. Dalam hal ini, Anda hanya perlu mendelegasikan ke superclass tersebut, untuk membuatnya melakukan apa yang biasanya dilakukannya.

Alexander - Kembalikan Monica
sumber
1
Sangat masuk akal bahwa konstruktor tidak diwarisi: Jika Anda menginisialisasi instance dari kelas turunan menggunakan penginisialisasi (mewarisi) dari kelas dasar, properti non-warisan yang baru ditentukan ("ditambahkan") oleh kelas turunan tidak akan pernah diinisialisasi.
Nicolas Miari
3
Sebenarnya, penginisialisasi diwarisi di Swift, asalkan Anda tidak menyediakan implementasi penginisialisasi Anda sendiri di subkelas Anda. Jika properti non-warisan yang baru Anda tentukan memiliki nilai default, Anda tidak perlu menulis penginisialisasi apa pun di subkelas dan hanya mewarisi semua penginisialisasi superclass Anda. Lihat di sini
TheBaj