Bermain-main dengan Swift, yang berasal dari latar belakang Java, mengapa Anda ingin memilih Struct daripada Class? Sepertinya mereka adalah hal yang sama, dengan Struct yang menawarkan fungsionalitas yang lebih sedikit. Mengapa memilihnya?
swift
class
struct
design-principles
bluedevil2k
sumber
sumber
Jawaban:
Menurut WWDC 2015 talk Protocol Oriented Programming di Swift ( video , transkrip ) yang sangat populer , Swift menyediakan sejumlah fitur yang membuat struct lebih baik daripada kelas dalam banyak keadaan.
Structs lebih disukai jika mereka relatif kecil dan copiable karena menyalin jauh lebih aman daripada memiliki banyak referensi ke contoh yang sama seperti yang terjadi pada kelas. Ini sangat penting ketika memberikan variabel ke banyak kelas dan / atau dalam lingkungan multithreaded. Jika Anda selalu dapat mengirim salinan variabel Anda ke tempat lain, Anda tidak perlu khawatir tentang tempat lain yang mengubah nilai variabel Anda di bawah Anda.
Dengan Structs, ada jauh lebih sedikit perlu khawatir tentang kebocoran memori atau beberapa utas berlomba untuk mengakses / memodifikasi satu contoh variabel. (Untuk yang lebih berpikiran teknis, pengecualian untuk itu adalah ketika menangkap struct di dalam penutupan karena itu sebenarnya menangkap referensi ke instance kecuali Anda secara eksplisit menandai itu untuk disalin).
Kelas juga bisa membengkak karena kelas hanya bisa mewarisi dari superkelas tunggal. Itu mendorong kami untuk membuat kacamata super besar yang mencakup banyak kemampuan berbeda yang hanya terkait secara longgar. Menggunakan protokol, terutama dengan ekstensi protokol di mana Anda dapat menyediakan implementasi protokol, memungkinkan Anda untuk menghilangkan kebutuhan kelas untuk mencapai perilaku semacam ini.
Pembicaraan menjabarkan skenario ini di mana kelas lebih disukai:
Ini menyiratkan bahwa struct harus menjadi default dan kelas harus menjadi mundur.
Di sisi lain, dokumentasi Bahasa Pemrograman Swift agak kontradiktif:
Di sini dikatakan bahwa kita harus menggunakan kelas secara default dan menggunakan struktur hanya dalam keadaan tertentu. Pada akhirnya, Anda perlu memahami implikasi dunia nyata dari tipe nilai vs tipe referensi dan kemudian Anda dapat membuat keputusan tentang kapan harus menggunakan struct atau kelas. Juga, perlu diingat bahwa konsep-konsep ini selalu berkembang dan dokumentasi Bahasa Pemrograman Swift ditulis sebelum ceramah Pemrograman Berorientasi Protokol diberikan.
sumber
In practice, this means that most custom data constructs should be classes, not structures.
Bisakah Anda menjelaskan kepada saya bagaimana, setelah membaca itu, Anda mendapatkan bahwa sebagian besar set data harus struktur dan bukan kelas? Mereka memberikan seperangkat aturan khusus ketika sesuatu harus menjadi struct dan cukup banyak mengatakan "semua skenario lain kelas lebih baik."Karena instance struct dialokasikan pada stack, dan instance kelas dialokasikan pada heap, struct kadang-kadang dapat secara drastis lebih cepat.
Namun, Anda harus selalu mengukurnya sendiri dan memutuskan berdasarkan kasus penggunaan unik Anda.
Pertimbangkan contoh berikut, yang menunjukkan 2 strategi pembungkus
Int
tipe data menggunakanstruct
danclass
. Saya menggunakan 10 nilai berulang untuk lebih mencerminkan dunia nyata, di mana Anda memiliki banyak bidang.Kinerja diukur menggunakan
Kode dapat ditemukan di https://github.com/knguyen2708/StructVsClassPerformance
UPDATE (27 Mar 2018) :
Mulai dari Swift 4.0, Xcode 9.2, menjalankan Release build di iPhone 6S, iOS 11.2.6, pengaturan Swift Compiler adalah
-O -whole-module-optimization
:class
versi butuh 2,06 detikstruct
versi butuh 4,17e-08 detik (50.000.000 kali lebih cepat)(Saya tidak lagi menjalankan berulang rata-rata, karena varians sangat kecil, di bawah 5%)
Catatan : perbedaannya jauh lebih dramatis tanpa optimasi seluruh modul. Saya akan senang jika seseorang dapat menunjukkan apa yang sebenarnya dilakukan oleh bendera.
PEMBARUAN (7 Mei 2016) :
Pada Swift 2.2.1, Xcode 7.3, menjalankan Release build di iPhone 6S, iOS 9.3.1, rata-rata lebih dari 5 run, pengaturan Swift Compiler adalah
-O -whole-module-optimization
:class
versi mengambil 2.159942142sstruct
versi mengambil 5.83E-08s (37.000.000 kali lebih cepat)Catatan : ketika seseorang menyebutkan bahwa dalam skenario dunia nyata, kemungkinan akan ada lebih dari 1 bidang dalam sebuah struct, saya telah menambahkan tes untuk struct / kelas dengan 10 bidang alih-alih 1. Anehnya, hasilnya tidak banyak berbeda.
HASIL ASLI (1 Juni 2014):
(Berlari pada struct / kelas dengan 1 bidang, bukan 10)
Pada Swift 1.2, Xcode 6.3.2, menjalankan Release build di iPhone 5S, iOS 8.3, rata-rata lebih dari 5 run
class
versi mengambil 9,788332333sstruct
versi mengambil 0,010532942s (900 kali lebih cepat)HASIL LAMA (dari waktu yang tidak diketahui)
(Berlari pada struct / kelas dengan 1 bidang, bukan 10)
Dengan rilis rilis di MacBook Pro saya:
class
Versi mengambil 1,10082 detikstruct
Versi mengambil 0,02324 detik (50 kali lebih cepat)sumber
Kesamaan antara struct dan kelas.
Saya membuat inti untuk ini dengan contoh-contoh sederhana. https://github.com/objc-swift/swift-classes-vs-strures
Dan perbedaan
1. Warisan.
struktur tidak dapat mewarisi dengan cepat. jika kamu mau
Pergi untuk kelas.
2. Lewati
Struktur cepat melewati nilai dan instance kelas melewati referensi.
Perbedaan Kontekstual
Konstanta dan variabel struktur
Contoh (Digunakan di WWDC 2014)
Menentukan struct yang disebut Point.
Sekarang jika saya mencoba mengubah x. Itu ungkapan yang valid.
Tetapi jika saya mendefinisikan suatu titik sebagai konstanta.
Dalam hal ini seluruh titik konstan konstan.
Jika saya menggunakan Point kelas sebagai gantinya ini adalah ekspresi yang valid. Karena dalam sebuah kelas konstanta yang tetap adalah referensi ke kelas itu sendiri, bukan variabel instansinya (Kecuali variabel-variabel tersebut didefinisikan sebagai konstanta)
sumber
Berikut adalah beberapa alasan lain untuk dipertimbangkan:
struct mendapatkan penginisialisasi otomatis yang tidak harus Anda pertahankan dalam kode sama sekali.
Untuk mendapatkan ini di kelas, Anda harus menambahkan inisialisasi, dan mempertahankan intializer ...
Jenis koleksi dasar seperti
Array
struct. Semakin banyak Anda menggunakannya dalam kode Anda sendiri, semakin Anda akan terbiasa melewati nilai dibandingkan dengan referensi. Contohnya:Rupanya kekekalan vs ketidakmampuan adalah topik yang besar, tetapi banyak orang pintar berpikir kekekalan - struct dalam hal ini - lebih disukai. Dapat berubah vs objek yang tidak berubah
sumber
internal
lingkup.mutating
Anda secara eksplisit tentang fungsi apa yang mengubah keadaan mereka. Tetapi sifat mereka sebagai tipe nilai adalah yang penting. Jika Anda mendeklarasikan struct denganlet
Anda tidak dapat memanggil fungsi yang bermutasi padanya. Video WWDC 15 tentang pemrograman yang lebih baik melalui tipe nilai adalah sumber yang bagus untuk ini.Dengan asumsi bahwa kita tahu Struct adalah tipe nilai dan Kelas adalah tipe referensi .
Jika Anda tidak tahu apa tipe nilai dan tipe referensi, lalu lihat Apa perbedaan antara meneruskan dengan referensi vs meneruskan dengan nilai?
Berdasarkan pos mikeash :
Saya pribadi tidak memberi nama kelas saya seperti itu. Saya biasanya menamai UserManager milik saya alih-alih UserController tetapi idenya sama
Selain itu, jangan gunakan kelas ketika Anda harus menimpa setiap contoh fungsi yaitu mereka tidak memiliki fungsionalitas bersama .
Jadi alih-alih memiliki beberapa subclass dari suatu kelas. Gunakan beberapa struct yang sesuai dengan protokol.
Kasus wajar lainnya untuk struct adalah ketika Anda ingin melakukan delta / diff dari model lama dan baru Anda. Dengan tipe referensi Anda tidak dapat melakukannya di luar kotak. Dengan tipe nilai, mutasi tidak dibagikan.
sumber
Beberapa keuntungan:
sumber
Struktur jauh lebih cepat daripada Kelas. Juga, jika Anda membutuhkan warisan maka Anda harus menggunakan Kelas. Poin paling penting adalah bahwa Kelas adalah tipe referensi sedangkan Struktur adalah tipe nilai. sebagai contoh,
sekarang mari kita buat instance dari keduanya.
sekarang mari kita lewati instance ini ke dua fungsi yang mengubah id, deskripsi, tujuan dll.
juga,
begitu,
sekarang jika kita mencetak id dan deskripsi flightA, kita dapatkan
Di sini, kita dapat melihat id dan deskripsi FlightA diubah karena parameter yang diteruskan ke metode modifikasi sebenarnya menunjuk ke alamat memori objek flightA (tipe referensi).
sekarang jika kita mencetak id dan deskripsi instance FLightB yang kita dapatkan,
Di sini kita dapat melihat bahwa instance FlightB tidak berubah karena dalam metode modFlight2, instance sebenarnya dari Flight2 melewati daripada referensi (tipe nilai).
sumber
Here we can see that the FlightB instance is not changed
Structs
sedangvalue type
danClasses
sedangreference type
Gunakan
value
tipe saat:Gunakan
reference
tipe saat:Informasi lebih lanjut dapat ditemukan di dokumentasi Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
informasi tambahan
Jenis nilai cepat disimpan di tumpukan. Dalam suatu proses, setiap utas memiliki ruang tumpukannya sendiri, jadi tidak ada utas lain yang dapat mengakses tipe nilai Anda secara langsung. Karenanya tidak ada kondisi balapan, kunci, deadlock atau kompleksitas sinkronisasi utas terkait.
Tipe nilai tidak memerlukan alokasi memori dinamis atau penghitungan referensi, yang keduanya merupakan operasi yang mahal. Pada saat yang sama metode pada tipe nilai dikirim secara statis. Ini menciptakan keuntungan besar dalam mendukung tipe nilai dalam hal kinerja.
Sebagai pengingat di sini adalah daftar Swift
Jenis nilai:
Jenis referensi:
sumber
Menjawab pertanyaan dari sudut pandang tipe nilai vs tipe referensi, dari posting blog Apple ini akan tampak sangat sederhana:
Seperti yang disebutkan dalam artikel itu, kelas tanpa properti yang dapat ditulisi akan berperilaku identik dengan struct, dengan (saya akan menambahkan) satu peringatan: struct adalah yang terbaik untuk model aman-thread - persyaratan yang semakin dekat dalam arsitektur aplikasi modern.
sumber
Dengan kelas Anda mendapatkan warisan dan diteruskan dengan referensi, struct tidak memiliki warisan dan diteruskan oleh nilai.
Ada sesi WWDC hebat di Swift, pertanyaan khusus ini dijawab dengan sangat terperinci di salah satunya. Pastikan Anda menontonnya, karena kecepatan Anda akan jauh lebih cepat daripada panduan Bahasa atau iBook.
sumber
Saya tidak akan mengatakan bahwa struct menawarkan fungsionalitas yang lebih sedikit.
Tentu, diri tidak dapat diubah kecuali dalam fungsi yang bermutasi, tetapi hanya itu saja.
Warisan bekerja dengan baik selama Anda tetap pada gagasan lama yang baik bahwa setiap kelas harus abstrak atau final.
Menerapkan kelas abstrak sebagai protokol dan kelas akhir sebagai struct.
Yang menyenangkan tentang struct adalah bahwa Anda dapat membuat bidang Anda bisa berubah tanpa membuat keadaan yang bisa dibagikan bersama karena copy on write menangani hal itu :)
Itu sebabnya properti / bidang dalam contoh berikut semuanya bisa berubah, yang tidak akan saya lakukan di Java atau C # atau kelas swift .
Contoh struktur pewarisan dengan sedikit penggunaan kotor dan langsung di bagian bawah dalam fungsi bernama "contoh":
sumber
Pola Penciptaan:
Dalam cepat, Struct adalah tipe nilai yang dikloning secara otomatis. Oleh karena itu kita mendapatkan perilaku yang diperlukan untuk menerapkan pola prototipe secara gratis.
Sedangkan kelas adalah tipe referensi, yang tidak secara otomatis dikloning selama penugasan. Untuk mengimplementasikan pola prototipe, kelas harus mengadopsi
NSCopying
protokol.Salinan duplikat hanya referensi, yang menunjuk ke objek-objek sedangkan salinan duplikat jauh referensi objek.
Menerapkan salinan dalam untuk setiap jenis referensi telah menjadi tugas yang membosankan. Jika kelas menyertakan tipe referensi lebih lanjut, kita harus menerapkan pola prototipe untuk masing-masing properti referensi. Dan kemudian kita harus benar-benar menyalin seluruh grafik objek dengan mengimplementasikan
NSCopying
protokol.Dengan menggunakan struct dan enum , kami membuat kode kami lebih sederhana karena kami tidak harus menerapkan logika salin.
sumber
Banyak Cocoa API membutuhkan subkelas NSObject, yang memaksa Anda menggunakan kelas. Tetapi selain itu, Anda dapat menggunakan case-case berikut dari blog Swift Apple untuk memutuskan apakah akan menggunakan tipe nilai struct / enum atau tipe referensi kelas.
https://developer.apple.com/swift/blog/?id=10
sumber
Satu hal yang tidak mendapat perhatian dalam jawaban ini adalah bahwa variabel yang memegang kelas vs sebuah struct bisa
let
sementara masih memungkinkan perubahan pada properti objek, sementara Anda tidak bisa melakukan ini dengan sebuah struct.Ini berguna jika Anda tidak ingin variabel menunjuk ke objek lain, tetapi masih perlu memodifikasi objek, yaitu dalam kasus memiliki banyak variabel instan yang ingin Anda perbarui satu demi satu. Jika itu adalah sebuah struct, Anda harus mengizinkan variabel untuk direset ke objek lain sama sekali menggunakan
var
untuk melakukan ini, karena tipe nilai konstan di Swift memungkinkan nol mutasi, sedangkan tipe referensi (kelas) tidak berperilaku seperti ini.sumber
Karena struct adalah tipe nilai dan Anda dapat membuat memori dengan sangat mudah yang menyimpan ke dalam stack. Bangunan dapat dengan mudah diakses dan setelah lingkup pekerjaan itu dengan mudah deallocated dari memori stack melalui pop dari atas stack. Di sisi lain kelas adalah tipe referensi yang menyimpan di heap dan perubahan yang dibuat dalam satu objek kelas akan berdampak pada objek lain karena mereka erat digabungkan dan tipe referensi. Semua anggota struktur adalah publik sedangkan semua anggota kelas adalah pribadi .
Kerugian dari struct adalah tidak dapat diwarisi.
sumber
Struktur dan kelas adalah tipe data yang ditentang pengguna
Secara default, struktur adalah publik sedangkan kelas adalah pribadi
Kelas mengimplementasikan prinsip enkapsulasi
Objek kelas dibuat pada memori tumpukan
Kelas digunakan untuk kegunaan kembali sedangkan struktur digunakan untuk mengelompokkan data dalam struktur yang sama
Struktur data anggota tidak dapat diinisialisasi secara langsung tetapi mereka dapat ditugaskan oleh luar struktur
Anggota data kelas dapat diinisialisasi secara langsung oleh konstruktor parameter kurang dan ditugaskan oleh konstruktor parameterized
sumber