Opsional dalam Swift adalah jenis yang dapat menyimpan nilai atau tidak ada nilai. Opsional ditulis dengan menambahkan a ?
ke jenis apa pun:
var name: String? = "Bertie"
Opsional (bersama dengan Generik) adalah salah satu konsep Swift yang paling sulit untuk dipahami. Karena cara mereka ditulis dan digunakan, mudah untuk mendapatkan ide yang salah tentang apa mereka. Bandingkan opsi di atas untuk membuat String normal:
var name: String = "Bertie" // No "?" after String
Dari sintaks sepertinya String opsional sangat mirip dengan String biasa. Ini bukan. String opsional bukan String dengan beberapa pengaturan "opsional" dihidupkan. Ini bukan jenis spesial String. String dan String opsional adalah tipe yang sama sekali berbeda.
Inilah hal yang paling penting untuk diketahui: Pilihan adalah sejenis wadah. String opsional adalah wadah yang mungkin berisi String. Int opsional adalah wadah yang mungkin mengandung Int. Pikirkan opsional sebagai semacam paket. Sebelum Anda membukanya (atau "membuka" dalam bahasa opsional) Anda tidak akan tahu apakah itu mengandung sesuatu atau tidak sama sekali.
Anda dapat melihat bagaimana opsional diterapkan di Perpustakaan Standar Swift dengan mengetikkan "Opsional" ke dalam file Swift apa pun dan mengkliknya. Inilah bagian penting dari definisi:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Opsional hanya merupakan enum
salah satu dari dua kasus: .none
atau .some
. Jika itu .some
, ada nilai terkait yang, dalam contoh di atas, akan menjadi String
"Halo". Pilihan menggunakan Generics untuk memberikan tipe ke nilai terkait. Jenis String opsional tidak String
, itu Optional
, atau lebih tepatnya Optional<String>
.
Segala sesuatu yang dilakukan Swift dengan opsional adalah keajaiban untuk membuat membaca dan menulis kode lebih lancar. Sayangnya ini mengaburkan cara kerjanya. Saya akan membahas beberapa trik nanti.
Catatan: Saya akan banyak berbicara tentang variabel opsional, tetapi tidak masalah untuk membuat konstanta opsional juga. Saya menandai semua variabel dengan tipe mereka untuk membuatnya lebih mudah untuk memahami tipe tipe yang sedang dibuat, tetapi Anda tidak harus dalam kode Anda sendiri.
Cara membuat opsional
Untuk membuat opsional, tambahkan ?
setelah jenis yang ingin Anda bungkus. Jenis apa pun bisa opsional, bahkan jenis kustom Anda sendiri. Anda tidak dapat memiliki ruang antara jenis dan ?
.
var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)
// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}
Menggunakan opsional
Anda dapat membandingkan opsi nil
untuk melihat apakah ada nilai:
var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
print("There is a name")
}
if name == nil { // Could also use an "else"
print("Name has no value")
}
Ini agak membingungkan. Ini menyiratkan bahwa opsional adalah satu atau lain hal. Entah nol atau "Bob". Ini tidak benar, opsional tidak berubah menjadi sesuatu yang lain. Membandingkannya dengan nol adalah trik untuk membuat kode yang lebih mudah dibaca. Jika opsional sama dengan nihil, ini berarti enum saat ini disetel ke .none
.
Hanya opsional yang boleh nol
Jika Anda mencoba mengatur variabel non-opsional menjadi nil, Anda akan mendapatkan kesalahan.
var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
Cara lain untuk melihat opsional adalah sebagai pelengkap variabel Swift normal. Mereka adalah counterpart ke variabel yang dijamin memiliki nilai. Swift adalah bahasa yang hati-hati yang membenci ambiguitas. Sebagian besar variabel didefinisikan sebagai non-opsional, tetapi kadang-kadang ini tidak mungkin. Misalnya, bayangkan pengontrol tampilan yang memuat gambar baik dari cache atau dari jaringan. Mungkin atau mungkin tidak memiliki gambar itu pada saat pengontrol tampilan dibuat. Tidak ada cara untuk menjamin nilai untuk variabel gambar. Dalam hal ini Anda harus membuatnya opsional. Dimulai saat nil
dan ketika gambar diambil, opsional mendapatkan nilai.
Menggunakan opsional mengungkapkan niat pemrogram. Dibandingkan dengan Objective-C, di mana objek apa pun bisa nihil, Swift perlu Anda jelaskan kapan nilai bisa hilang dan kapan dijamin ada.
Untuk menggunakan opsional, Anda "membuka"
Opsional String
tidak dapat digunakan sebagai pengganti yang sebenarnya String
. Untuk menggunakan nilai terbungkus di dalam opsional, Anda harus membukanya. Cara termudah untuk membuka bungkusan opsional adalah dengan menambahkan !
setelah nama opsional. Ini disebut "force unrapping". Ini mengembalikan nilai di dalam opsional (sebagai tipe asli) tetapi jika opsional nil
, itu menyebabkan crash runtime. Sebelum membuka bungkus, Anda harus yakin ada nilainya.
var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")
name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.
Memeriksa dan menggunakan opsional
Karena Anda harus selalu memeriksa nol sebelum membuka dan menggunakan opsional, ini adalah pola umum:
var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
let unwrappedMealPreference: String = mealPreference!
print("Meal: \(unwrappedMealPreference)") // or do something useful
}
Dalam pola ini Anda memeriksa bahwa ada nilai, maka ketika Anda yakin itu, Anda memaksa membuka itu menjadi konstanta sementara untuk digunakan. Karena ini adalah hal yang biasa dilakukan, Swift menawarkan jalan pintas menggunakan "jika dibiarkan". Ini disebut "penjilidan opsional".
var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
print("Meal: \(unwrappedMealPreference)")
}
Ini menciptakan konstanta sementara (atau variabel jika Anda ganti let
dengan var
) yang cakupannya hanya di dalam kurung if. Karena harus menggunakan nama seperti "unwrappedMealPreference" atau "realMealPreference" adalah beban, Swift memungkinkan Anda untuk menggunakan kembali nama variabel asli, membuat nama temporer dalam lingkup braket
var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // separate from the other mealPreference
}
Berikut beberapa kode untuk menunjukkan bahwa variabel yang berbeda digunakan:
var mealPreference: String? = "Vegetarian"
if var mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
mealPreference = "Beef" // No effect on original
}
// This is the original mealPreference
print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"
Ikatan opsional berfungsi dengan memeriksa untuk melihat apakah opsional sama dengan nol. Jika tidak, ia membuka opsi ke konstanta yang disediakan dan mengeksekusi blok. Dalam Xcode 8.3 dan yang lebih baru (Swift 3.1), mencoba untuk mencetak opsional seperti ini akan menyebabkan peringatan yang tidak berguna. Gunakan opsional debugDescription
untuk membungkamnya:
print("\(mealPreference.debugDescription)")
Apa itu opsional?
Opsional memiliki dua kasus penggunaan:
- Hal-hal yang dapat gagal (saya mengharapkan sesuatu tetapi saya tidak mendapatkan apa-apa)
- Hal-hal yang bukan apa-apa sekarang tetapi mungkin sesuatu nanti (dan sebaliknya)
Beberapa contoh nyata:
- Properti yang bisa ada di sana atau tidak di sana, suka
middleName
atau spouse
di Person
kelas
- Metode yang dapat mengembalikan nilai atau tidak sama sekali, seperti mencari kecocokan dalam array
- Metode yang dapat mengembalikan hasil atau mendapatkan kesalahan dan tidak menghasilkan apa-apa, seperti mencoba membaca konten file (yang biasanya mengembalikan data file) tetapi file tersebut tidak ada
- Mendelegasikan properti, yang tidak selalu harus ditetapkan dan umumnya ditetapkan setelah inisialisasi
- Untuk
weak
properti di kelas. Hal yang mereka tunjuk dapat diatur nil
kapan saja
- Sumber daya besar yang mungkin harus dilepaskan untuk mendapatkan kembali memori
- Saat Anda membutuhkan cara untuk mengetahui kapan suatu nilai telah ditetapkan (data belum dimuat> data) alih-alih menggunakan data yang terpisahDiisi
Boolean
Opsional tidak ada di Objective-C tetapi ada konsep yang setara, mengembalikan nol. Metode yang dapat mengembalikan objek dapat mengembalikan nol. Ini berarti "tidak adanya objek yang valid" dan sering digunakan untuk mengatakan bahwa ada sesuatu yang salah. Ini hanya bekerja dengan objek Objective-C, bukan dengan primitif atau tipe-C dasar (enum, struct). Objective-C sering memiliki tipe khusus untuk mewakili tidak adanya nilai-nilai ini ( NSNotFound
yang sebenarnya NSIntegerMax
, kCLLocationCoordinate2DInvalid
untuk mewakili koordinat yang tidak valid, -1
atau beberapa nilai negatif juga digunakan). Coder harus mengetahui nilai-nilai khusus ini sehingga harus didokumentasikan dan dipelajari untuk setiap kasus. Jika suatu metode tidak dapat mengambil nil
sebagai parameter, ini harus didokumentasikan. Di Objective-C,nil
adalah pointer sama seperti semua objek didefinisikan sebagai pointer, tetapi nil
menunjuk ke alamat (nol) tertentu. Dalam bahasa Swift, nil
adalah literal yang berarti tidak adanya tipe tertentu.
Dibandingkan dengan nil
Anda dulu dapat menggunakan opsi apa pun sebagai Boolean
:
let leatherTrim: CarExtras? = nil
if leatherTrim {
price = price + 1000
}
Dalam versi terbaru Swift yang harus Anda gunakan leatherTrim != nil
. Kenapa ini? Masalahnya adalah bahwa Boolean
dapat dibungkus dengan opsional. Jika sudah Boolean
seperti ini:
var ambiguous: Boolean? = false
ia memiliki dua jenis "salah", satu di mana tidak ada nilai dan satu di mana ia memiliki nilai tetapi nilainya false
. Swift membenci ambiguitas jadi sekarang Anda harus selalu memeriksa opsi menentang nil
.
Anda mungkin bertanya-tanya apa gunanya opsional Boolean
? Seperti dengan opsional lainnya .none
negara dapat menunjukkan bahwa nilainya belum diketahui. Mungkin ada sesuatu di ujung lain dari panggilan jaringan yang membutuhkan waktu untuk polling. Boolean Opsional juga disebut " Boolean Tiga Nilai "
Trik cepat
Swift menggunakan beberapa trik untuk memungkinkan opsional berfungsi. Pertimbangkan tiga baris kode opsional yang tampak biasa ini;
var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }
Tak satu pun dari baris ini harus dikompilasi.
- Baris pertama menetapkan String opsional menggunakan String literal, dua jenis berbeda. Kalaupun ini adalah
String
tipe yang berbeda
- Baris kedua menetapkan String opsional menjadi nihil, dua jenis berbeda
- Baris ketiga membandingkan string opsional dengan nol, dua tipe berbeda
Saya akan membahas beberapa detail implementasi opsional yang memungkinkan baris ini bekerja.
Membuat opsional
Menggunakan ?
untuk membuat opsional adalah gula sintaksis, diaktifkan oleh kompiler Swift. Jika Anda ingin melakukannya jauh, Anda dapat membuat opsional seperti ini:
var name: Optional<String> = Optional("Bob")
Optional
Inisialisasi pertama panggilan ini public init(_ some: Wrapped)
, yang menyimpulkan jenis yang terkait opsional dari jenis yang digunakan dalam tanda kurung.
Cara lebih lama untuk membuat dan mengatur opsional:
var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")
Pengaturan opsional untuk nil
Anda dapat membuat opsional tanpa nilai awal, atau membuatnya dengan nilai awal nil
(keduanya memiliki hasil yang sama).
var name: String?
var name: String? = nil
Mengizinkan opsional sama nil
dengan diaktifkan oleh protokol ExpressibleByNilLiteral
(sebelumnya bernama NilLiteralConvertible
). Opsional dibuat dengan Optional
penginisialisasi kedua public init(nilLiteral: ())
,. Dokumen mengatakan bahwa Anda tidak boleh menggunakan ExpressibleByNilLiteral
apa pun kecuali opsional, karena itu akan mengubah arti nihil dalam kode Anda, tetapi dimungkinkan untuk melakukannya:
class Clint: ExpressibleByNilLiteral {
var name: String?
required init(nilLiteral: ()) {
name = "The Man with No Name"
}
}
let clint: Clint = nil // Would normally give an error
print("\(clint.name)")
Protokol yang sama memungkinkan Anda untuk mengatur opsional yang sudah dibuat nil
. Meskipun tidak disarankan, Anda dapat menggunakan penginisialisasi literal nil secara langsung:
var name: Optional<String> = Optional(nilLiteral: ())
Membandingkan opsional untuk nil
Opsional menentukan dua operator "==" dan "! =" Khusus, yang dapat Anda lihat dalam Optional
definisi. Yang pertama ==
memungkinkan Anda untuk memeriksa apakah ada opsi yang sama dengan nol. Dua opsional berbeda yang disetel ke .tidak akan selalu sama jika jenis terkait adalah sama. Ketika Anda membandingkan dengan nol, di belakang layar Swift membuat opsional dari jenis terkait yang sama, diatur ke. Tidak ada yang kemudian menggunakannya untuk perbandingan.
// How Swift actually compares to nil
var tuxedoRequired: String? = nil
let temp: Optional<String> = Optional.none
if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
print("tuxedoRequired is nil")
}
==
Operator kedua memungkinkan Anda untuk membandingkan dua opsional. Keduanya harus tipe yang sama dan tipe tersebut harus sesuai Equatable
(protokol yang memungkinkan membandingkan hal-hal dengan operator "==" reguler). Swift (mungkin) membuka dua nilai dan membandingkannya secara langsung. Ini juga menangani case di mana satu atau kedua opsional berada .none
. Perhatikan perbedaan antara membandingkan dengan nil
literal.
Selain itu, ini memungkinkan Anda untuk membandingkan Equatable
jenis apa pun dengan pembungkus opsional jenis itu:
let numberToFind: Int = 23
let numberFromString: Int? = Int("23") // Optional(23)
if numberToFind == numberFromString {
print("It's a match!") // Prints "It's a match!"
}
Di belakang layar, Swift membungkus non-opsional sebagai opsional sebelum perbandingan. Ia bekerja dengan literal juga ( if 23 == numberFromString {
)
Saya mengatakan ada dua ==
operator, tetapi sebenarnya ada sepertiga yang memungkinkan Anda untuk menempatkan nil
di sisi kiri perbandingan
if nil == name { ... }
Memilih Penamaan
Tidak ada konvensi Swift untuk penamaan tipe opsional berbeda dari tipe non-opsional. Orang menghindari menambahkan sesuatu ke nama untuk menunjukkan bahwa itu adalah opsional (seperti "opsionalMiddleName", atau "possibleNumberAsString") dan biarkan deklarasi menunjukkan bahwa itu adalah tipe opsional. Ini menjadi sulit ketika Anda ingin memberi nama sesuatu untuk menyimpan nilai dari opsional. Nama "middleName" menyiratkan bahwa itu adalah tipe String, jadi ketika Anda mengekstrak nilai String dari itu, Anda sering dapat berakhir dengan nama-nama seperti "actualMiddleName" atau "unwrappedMiddleName" atau "realMiddleName". Gunakan penjilidan opsional dan gunakan kembali nama variabel untuk menyiasatinya.
Definisi resmi
Dari "The Basics" dalam Bahasa Pemrograman Swift :
Swift juga memperkenalkan tipe opsional, yang menangani ketiadaan nilai. Opsional mengatakan "ada nilai, dan sama dengan x" atau "tidak ada nilai sama sekali". Opsional mirip dengan menggunakan nil dengan pointer di Objective-C, tetapi mereka bekerja untuk semua jenis, bukan hanya kelas. Opsional lebih aman dan lebih ekspresif daripada petunjuk nol di Objective-C dan merupakan jantung dari banyak fitur Swift yang paling kuat.
Opsional adalah contoh dari fakta bahwa Swift adalah jenis bahasa yang aman. Swift membantu Anda memperjelas jenis nilai yang dapat digunakan oleh kode Anda. Jika bagian dari kode Anda mengharapkan sebuah String, ketik safety mencegah Anda melewatkannya dengan Int. Ini memungkinkan Anda untuk menangkap dan memperbaiki kesalahan sedini mungkin dalam proses pengembangan.
Untuk menyelesaikannya, inilah puisi dari tahun 1899 tentang opsional:
Kemarin di tangga
saya bertemu dengan seorang pria yang tidak ada di sana.
Dia tidak ada di sana lagi hari ini
saya berharap, saya berharap dia akan pergi
Antigonish
Sumber lainnya: