Pastikan sifat yang digunakan untuk kesamaan dan pencincangan kekal stabil selagi objek menjadi kunci kamus. Untuk pengiraan Swift setempat pada Mac anda, pilih identiti objek, ID tidak berubah atau petikan nilai berdasarkan maksud carian yang dikehendaki.
Panduan ini menggunakan segi empat tepat sintetik dan pengiraan luas kecil yang disimpan sepenuhnya dalam memori. Tugas praktikalnya ialah membolehkan label atau dimensi segi empat tepat berubah tanpa kehilangan entri kamus yang berkaitan—dan tanpa mengelirukan carian yang berjaya dengan pengiraan yang terkini.
Mengapa perubahan pada objek boleh merosakkan carian kamus?
Dictionary Swift ialah jadual cincang dengan kunci yang mematuhi Hashable. Subskrip berasaskan kunci memulangkan nilai pilihan kerana kunci yang diminta mungkin tiada. Pencincangan membantu mencari entri; kesamaan menentukan kunci yang dianggap setara. Lihat dokumentasi Dictionary Apple.
Kontrak Hashable menghubungkan kedua-dua operasi ini: tika yang sama mesti memasukkan komponen penting yang sama ke dalam Hasher, mengikut susunan yang sama. Apabila melaksanakan kunci tersuai, gunakan sifat yang mentakrifkan kesamaan dalam hash(into:). Apple menerangkan keperluan ini dalam Hashable.
Kunci jenis rujukan membawa satu lagi perkara untuk dipertimbangkan. Apabila tika kelas diberikan, rujukan kepada objek sedia ada dihantar, bukannya objek berasingan dicipta. Mengisytiharkan rujukan dengan let tidak membekukan sifatnya yang boleh berubah. Peraturan bahasa ini didokumenkan dalam Structures and Classes.
Katakan kelas segi empat tepat membandingkan dan mencincang labelnya yang boleh diedit. Anda memasukkan objek ketika labelnya ialah \"Tile A\", kemudian menukar label itu kepada \"Tile B\". Rujukan yang disimpan itu kini mendedahkan keadaan penentu kunci yang berbeza. Kedudukan asal dalam kamus dan carian kemudian tidak lagi mempunyai padanan yang stabil.
Peraturan reka bentuk ini berpunca daripada kontrak jadual cincang dan kesamaan: pastikan keadaan yang menentukan kesamaan kunci kekal stabil sepanjang kunci itu berada dalam kamus. Apple menyatakan keperluan sepadan secara jelas untuk objek Foundation: cincangan objek tidak boleh berubah selagi objek itu berada dalam koleksi yang menggunakan cincangan tersebut untuk penempatan. Lihat NSObjectProtocol.hash.
Jangan bina ujian berdasarkan andaian bahawa setiap perubahan tidak sah akan serta-merta memulangkan nil. Setelah kontrak kunci dilanggar, carian yang kelihatan berjaya tidak cukup untuk membuktikan reka bentuk itu berfungsi. Sebaliknya, uji peraturan kesamaan yang dimaksudkan dan tingkah laku yang stabil.
Kunci yang manakah sesuai dengan tugas ini?
Pilih maksud entri kamus sebelum menulis pematuhan protokol. Untuk alat pengiraan luas segi empat tepat, tiga soalan ini membawa kepada reka bentuk yang berbeza:
| Apakah yang patut diwakili oleh entri? | Kunci yang sesuai | Apakah yang berlaku apabila segi empat tepat berubah? |
|---|---|---|
| Objek segi empat tepat yang khusus ini | Kunci kelas berasaskan identiti | Entri kekal dikaitkan dengan tika itu |
| Segi empat tepat logik yang dikenal pasti secara berasingan daripada objek semasanya | Nilai ID tidak berubah | Entri kekal dikaitkan dengan ID itu |
| Pengiraan untuk dimensi tertentu | Petikan dimensi tidak berubah | Dimensi yang berubah menghasilkan kunci carian yang berbeza |
Jadual ini ialah panduan reka bentuk yang berasaskan peraturan identiti, nilai dan pencincangan Swift. Ia juga membezakan dua tanggungjawab: memastikan perkaitan boleh dicapai dan memastikan hasil pengiraan kekal terkini. Kunci identiti yang stabil menyelesaikan perkara pertama; pengiraan semula atau petikan input yang lengkap menangani perkara kedua.
Gunakan identiti objek apabila entri milik satu tika
Untuk perkaitan dalam memori yang patut mengikuti satu objek melalui suntingan, takrifkan kesamaan menggunakan === dan cincang ObjectIdentifier-nya. Operator identiti Swift menguji sama ada rujukan menunjuk kepada tika yang sama. Apple mendokumenkan ObjectIdentifier sebagai pengecam boleh dicincang yang perbandingannya sah sepanjang hayat tika. Lihat Structures and Classes dan ObjectIdentifier.
Berikut ialah program segerak yang lengkap. Program ini memerlukan Mac dengan alat baris perintah Swift tersedia; ia hanya menggunakan pustaka standard Swift yang diimport secara automatik. Simpan sebagai IdentityKeys.swift dan jalankan swift IdentityKeys.swift dalam Terminal. Contoh-contoh ini telah diperiksa tetapi tidak dapat dilaksanakan dalam persekitaran penyelidikan kerana Swift tidak tersedia.
final class Rectangle: Hashable {
var label: String
var width: Int
var height: Int
init(label: String, width: Int, height: Int) {
self.label = label
self.width = width
self.height = height
}
static func == (lhs: Rectangle, rhs: Rectangle) -> Bool {
lhs === rhs
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
func demonstrateIdentityKeys() {
let rectangle = Rectangle(label: "Tile A", width: 4, height: 3)
let alias = rectangle
let separate = Rectangle(label: "Tile A", width: 4, height: 3)
var areas: [Rectangle: Int] = [
rectangle: rectangle.width * rectangle.height
]
rectangle.label = "Tile B"
assert(areas[alias] == 12)
assert(areas[separate] == nil)
rectangle.width = 5
assert(areas[rectangle] == 12) // Still the previous calculation.
areas[rectangle] = rectangle.width * rectangle.height
assert(areas[rectangle] == 15)
assert(areas.count == 1)
print("Renamed lookup: 12")
print("Recalculated area: \(areas[rectangle]!)")
}
demonstrateIdentityKeys()
Output yang dijangka ialah Renamed lookup: 12, diikuti Recalculated area: 15. Pernyataan pengesahan menyatakan kontrak yang berguna: alias menemui entri sedia ada, segi empat tepat yang dicipta secara berasingan tidak menemuinya, dan penyuntingan objek asal tidak menghasilkan kunci lain.
Perubahan lebar itu sengaja membiarkan luas yang disimpan kekal pada 12 sehingga program menetapkan 15. Identiti kekal stabil, tetapi kamus tidak memantau kebergantungan aritmetik atau mengira semula nilai. Tentukan sama ada aplikasi anda mengemas kini nilai sejurus selepas suntingan atau membuangnya sehingga pengiraan seterusnya.
Kesamaan identiti ialah keputusan yang terpakai pada seluruh jenis. Setiap perbandingan == pada kelas ini kini bermaksud “tika yang sama.” Jika model sudah mentakrifkan kesamaan berdasarkan dimensi atau nilai lain, gunakan reka bentuk kunci berasingan dan bukannya mengubah maksud tersebut hanya untuk satu kamus.
Anda juga boleh menggunakan ObjectIdentifier secara langsung sebagai kunci kamus, tetapi pengecam itu sahaja tidak menyediakan segi empat tepat yang diperlukan untuk operasi kemudian. Pastikan objek berkaitan kekal hidup selagi pengecamnya digunakan untuk perbandingan, dan buang entri yang tidak lagi diperlukan. Had hayat yang didokumenkan menjadikannya mekanisme identiti dalam memori yang terhad skopnya. Lihat ObjectIdentifier.
Gunakan petikan nilai apabila entri mewakili pengiraan
Untuk hasil luas yang boleh digunakan semula, dimensi itu sendiri selalunya merupakan kunci yang sesuai. Dua segi empat tepat berukuran 4 kali 3 boleh berkongsi hasil yang sama walaupun kedua-duanya objek berasingan dengan label berbeza. Perubahan dimensi patut memilih entri yang berbeza.
Struktur Swift mempunyai semantik nilai, dan pengkompil boleh menjana pematuhan Hashable secara automatik apabila sifat tersimpannya boleh dicincang. Oleh itu, petikan yang mengandungi integer tidak berubah memberikan kunci yang mudah untuk pengiraan ini. Lihat Structures and Classes dan Hashable.
Jalankan alternatif lengkap ini secara berasingan dengan swift SnapshotKeys.swift. Input sintetiknya ialah integer positif yang kecil, jadi contoh ini tidak memerlukan antara muka input angka serba guna.
final class EditableRectangle {
var label: String
var width: Int
var height: Int
init(label: String, width: Int, height: Int) {
self.label = label
self.width = width
self.height = height
}
}
struct AreaInputs: Hashable, Sendable {
let width: Int
let height: Int
}
func demonstrateSnapshotKeys() {
let rectangle = EditableRectangle(
label: "Tile A", width: 4, height: 3
)
var areas: [AreaInputs: Int] = [:]
let original = AreaInputs(
width: rectangle.width, height: rectangle.height
)
areas[original] = original.width * original.height
rectangle.label = "Tile B"
rectangle.width = 5
let current = AreaInputs(
width: rectangle.width, height: rectangle.height
)
assert(areas[original] == 12)
assert(areas[current] == nil)
areas[current] = current.width * current.height
assert(areas[current] == 15)
assert(areas.count == 2)
print("Original area: \(areas[original]!)")
print("Current area: \(areas[current]!)")
}
demonstrateSnapshotKeys()
Output yang dijangka ialah Original area: 12, kemudian Current area: 15. Kedua-dua hasil kekal boleh dicapai kerana program memasukkan dua petikan yang berbeza. Hasil lama masih menerangkan dimensi lama dengan tepat.
Sertakan setiap input yang mempengaruhi pengiraan dalam petikan. Label tidak relevan kepada luas segi empat tepat, jadi label tidak disertakan di sini. Jika pengiraan kemudian bergantung pada tetapan lain, tambahkan tetapan itu pada kunci. Jika tidak, pengiraan yang berbeza mungkin berkongsi entri secara salah. Ini keperluan reka bentuk pengiraan yang berasingan daripada pematuhan kunci terhadap Hashable.
Struktur yang sekadar membungkus rujukan kelas boleh ubah tidak secara automatik menghasilkan petikan beku bagi objek itu. Salin nilai yang berkaitan ke dalam petikan, seperti yang dilakukan oleh contoh ini. Tentukan juga bila hendak membuang petikan lama jika suntingan berulang akan mengumpulkan hasil yang tidak lagi diperlukan.
Bilakah anda patut menggunakan ID tidak berubah?
Gunakan ID apabila beberapa tika objek patut mengakses entri logik yang sama. Untuk alat sintetik ini, ID integer tidak berubah mungkin memadai: simpan hasil dalam [Int: Int], gunakan let id: Int milik segi empat tepat untuk mengaksesnya, dan kekalkan label serta dimensi yang boleh diedit di luar kunci.
Dengan reka bentuk itu, dua objek dengan ID 7 sengaja mengakses entri kamus yang sama. Peraturan pembinaan anda mesti memastikan segi empat tepat logik yang berbeza tidak menerima ID yang sama secara tidak sengaja. Mengemas kini kunci sedia ada akan menggantikan nilai yang dikaitkan dengannya, seperti yang didokumenkan dalam Dictionary.
Jadual luas berasaskan ID masih memerlukan dasar kemas kini selepas dimensi berubah. Jika anda mahukan hasil untuk setiap set input yang berbeza, gunakan petikan. Jika anda hanya mahukan hasil terkini bagi setiap segi empat tepat logik, ID bersama pengiraan semula yang jelas lebih sesuai.
Adakah kamus Foundation atau aktor menyelesaikan masalah kunci boleh ubah?
Foundation mempunyai kontrak pemasukan yang berbeza. NSMutableDictionary.setObject(_:forKey:) memerlukan kunci NSCopying dan menyalinnya semasa pemasukan. Salinan kunci boleh membentuk petikan jika pelaksanaan penyalinannya menyediakan keadaan kunci yang stabil. Carian seterusnya mesti sepadan dengan salinan yang disimpan itu. Lihat setObject(_:forKey:) Apple.
Jangan simpulkan daripada tingkah laku ini bahawa kamus natif Swift membuat salinan bebas bagi tika kelas yang digunakan sebagai kunci. Penugasan kelas Swift menggunakan semantik rujukan. Bagi kunci Foundation tersuai, sahkan tingkah laku penyalinan, kesamaan dan pencincangan secara bersama; keperluan hash Foundation tetap terpakai.
Keserentakan ialah perkara yang berasingan. Aktor menyelaraskan akses kepada keadaan boleh ubah yang diasingkannya, dan Sendable menerangkan nilai yang boleh merentas domain keserentakan tertakluk pada keperluannya. Kedua-duanya tidak mengubah maksud kesamaan kunci atau membaiki kunci yang keadaan penentunya berubah selepas pemasukan. Lihat bab Concurrency Swift dan dokumentasi Sendable Apple.
Contoh petikan mengisytiharkan Sendable secara jelas kerana integer tersimpannya memenuhi keperluan protokol untuk jenis nilai. Jika kemudian anda memindahkan pengiraan luas ke dalam aktor, hantar petikan lengkap dan biarkan aktor memiliki kamus hasilnya. Segi empat tepat boleh ubah boleh kekal dalam domain pengasingannya yang sedia ada. Contoh segerak ini tidak memerlukan aktor.
Semak kontrak sebelum bergantung pada carian
Gunakan jujukan kecil dan deterministik untuk mengesahkan reka bentuk yang dipilih:
- Masukkan entri dan sahkan carian awalnya.
- Ubah sifat yang dikecualikan daripada kesamaan dan pencincangan; sahkan bahawa entri yang sama masih boleh dicapai.
- Bandingkan alias dengan objek atau petikan yang dicipta secara berasingan; sahkan hasilnya menepati maksud kesamaan yang dikehendaki.
- Ubah input pengiraan; sahkan bahawa hasil dikemas kini, dibuang atau dicapai melalui petikan baharu.
- Semak bilangan entri kamus supaya penggantian dan pemasukan berlaku seperti yang dimaksudkan.
Jika pelaksanaan sedia ada mengubah keadaan penentu kunci, baiki model kunci sebelum bergantung pada carian seterusnya. Untuk pengiraan setempat yang kecil ini, membina semula hasil daripada senarai input sintetik yang berasingan lebih jelas daripada menganggap operasi kamus seterusnya sebagai mekanisme pembaikan. Simpan input asal yang mencukupi untuk mengira semula, dan bukannya menggugurkan secara senyap entri yang tidak dapat ditemui.
Pilih identiti untuk perkaitan dengan satu objek hidup, ID tidak berubah untuk satu item logik, dan petikan nilai untuk satu pengiraan. Kemudian tetapkan dasar kemas kini hasil dengan jelas. Keputusan-keputusan ini bersama-sama memastikan carian kekal bermakna apabila segi empat tepat yang boleh diedit berubah.
Sumber
- Hashable — Apple
- Dictionary — Apple
- Structures and Classes — Swift.org
- ObjectIdentifier — Apple
- hash — Apple
- setObject(_:forKey:) — Apple
- Concurrency — Swift.org
- Sendable — Apple