Apa itu "nilai yang belum dibuka" di Swift?

155

Saya sedang belajar Swift untuk iOS 8 / OSX 10.10 dengan mengikuti tutorial ini , dan istilah " nilai terbuka " digunakan beberapa kali, seperti pada paragraf ini (di bawah Objek dan Kelas ):

Ketika bekerja dengan nilai-nilai opsional, Anda dapat menulis? sebelum operasi seperti metode, properti, dan berlangganan. Jika nilainya sebelum? adalah nihil, semuanya setelah? diabaikan dan nilai keseluruhan ekspresi adalah nihil. Jika tidak, nilai opsional tidak terbuka , dan semuanya setelah? bertindak berdasarkan nilai yang tidak terbuka . Dalam kedua kasus, nilai keseluruhan ekspresi adalah nilai opsional.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

Saya tidak mengerti, dan mencari di web tanpa hasil.

Apa artinya ini?


Edit

Dari jawaban Cezary, ada sedikit perbedaan antara output dari kode asli dan solusi akhir (diuji di taman bermain):

Kode asli

Kode asli

Solusi Cezary

Solusi Cezary

Properti superclass ditampilkan dalam output dalam kasus kedua, sementara ada objek kosong dalam kasus pertama.

Bukankah hasilnya seharusnya sama dalam kedua kasus?

T&J terkait: Berapakah nilai opsional di Swift?

Besar
sumber
1
Jawaban Cezary tepat. Juga, ada buku intro yang bagus untuk Swift tersedia secara GRATIS di iBooks
Daniel Storm
@DanielStormApps iBook ini adalah versi portabel dari tutorial terkait di pertanyaan saya :)
Bigood

Jawaban:

289

Pertama, Anda harus memahami apa jenis Opsional. Tipe opsional pada dasarnya berarti bahwa variabel tersebut dapat nil .

Contoh:

var canBeNil : Int? = 4
canBeNil = nil

Tanda tanya menunjukkan fakta yang canBeNilbisa nil.

Ini tidak akan berfungsi:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

Untuk mendapatkan nilai dari variabel Anda jika itu opsional, Anda harus membuka bungkusnya . Ini berarti menempatkan tanda seru di bagian akhir.

var canBeNil : Int? = 4
println(canBeNil!)

Kode Anda akan terlihat seperti ini:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

Sidenote:

Anda juga dapat mendeklarasikan opsi untuk membuka secara otomatis dengan menggunakan tanda seru alih-alih tanda tanya.

Contoh:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

Jadi cara alternatif untuk memperbaiki kode Anda adalah:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

EDIT:

Perbedaan yang Anda lihat adalah persis gejala fakta bahwa nilai opsional dibungkus . Ada lapisan lain di atasnya. Versi yang tidak terbuka hanya menunjukkan objek yang lurus karena, yah, tidak terbuka.

Perbandingan bermain cepat:

tempat bermain

Dalam kasus pertama dan kedua, objek tidak secara otomatis dibuka, sehingga Anda melihat dua "lapisan" ( {{...}}), sedangkan dalam kasus ketiga, Anda hanya melihat satu lapisan ( {...}) karena objek sedang dibuka secara otomatis.

Perbedaan antara kasing pertama dan kasing kedua adalah bahwa kasing kedua akan memberikan Anda kesalahan runtime jika optionalSquarediatur ke nil. Menggunakan sintaks dalam kasus pertama, Anda dapat melakukan sesuatu seperti ini:

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}
Cezary Wojcik
sumber
1
Terima kasih atas penjelasan yang jelas! Kode yang termasuk dalam pertanyaan saya dikutip dari tutorial Apple (tautan dalam pertanyaan), dan saya kira itu seharusnya berfungsi apa adanya (dan memang demikian). Padahal, solusi akhir Anda dan yang asli menghasilkan hasil yang berbeda (lihat hasil edit saya). Adakah pemikiran?
Bigood
2
Keren. Penjelasan yang bagus. Saya masih bingung. Mengapa saya ingin menggunakan nilai opsional? Kapan itu berguna? Mengapa Apple menciptakan perilaku dan sintaksis ini?
Todd Lehman
1
Ini sangat relevan ketika datang ke properti kelas. Swift mengharuskan semua metode non-opsional diinisialisasi dalam init()fungsi kelas. Saya merekomendasikan membaca bab "Dasar-Dasar" (mencakup opsional), "Inisialisasi", dan "Rantai Opsional" dalam buku Swift yang dirilis oleh Apple.
Cezary Wojcik
2
@ToddLehman Apple membuat ini untuk membantu Anda menulis kode yang jauh lebih aman. Misalnya bayangkan semua pengecualian pointer nol di Jawa menabrak suatu program karena seseorang lupa untuk memeriksa null. Atau perilaku aneh di Objective-C karena Anda lupa memeriksa nol. Dengan tipe opsional, Anda tidak boleh lupa untuk berurusan dengan nol. Compiler memaksa Anda untuk menghadapinya.
Erik Engheim
5
@ AdamSmith - Berasal dari latar belakang Java, saya pasti dapat melihat penggunaan opsional ... tetapi juga datang (baru-baru ini) dari latar belakang Objective-C, saya masih kesulitan melihat penggunaannya, karena Objective- C secara eksplisit memungkinkan Anda untuk mengirim pesan ke objek nil. Hmm. Saya ingin melihat seseorang menulis contoh ini yang menunjukkan bagaimana mereka membantu membuat kode lebih pendek dan lebih aman daripada kode setara yang tidak menggunakannya. Saya tidak mengatakan mereka tidak berguna; Saya hanya mengatakan saya belum "mengerti".
Todd Lehman
17

Jawaban yang benar yang ada sangat bagus, tetapi saya menemukan bahwa bagi saya untuk memahami ini sepenuhnya, saya memerlukan analogi yang baik, karena ini adalah konsep yang sangat abstrak dan aneh.

Jadi, izinkan saya membantu pengembang (berpikir visual) sesama "berotak kanan" dengan memberikan perspektif berbeda selain jawaban yang benar. Ini analogi bagus yang banyak membantu saya.

Analogi Pembungkus Hadiah Ulang Tahun

Pikirkan opsional sebagai hadiah ulang tahun yang datang dalam pembungkus yang kaku, keras, berwarna.

Anda tidak tahu apakah ada sesuatu di dalam bungkus sampai Anda membuka kado - mungkin tidak ada apa-apa di dalamnya! Jika ada sesuatu di dalamnya, itu bisa jadi hadiah lain, yang juga dibungkus, dan yang juga mungkin tidak mengandung apa pun . Anda bahkan dapat membuka 100 hadiah bersarang untuk akhirnya menemukan tidak ada yang lain selain membungkus .

Jika nilai opsional tidak nil, sekarang Anda telah mengungkapkan kotak yang berisi sesuatu . Tetapi, terutama jika nilainya tidak diketik secara eksplisit dan merupakan variabel dan bukan konstanta yang telah ditentukan, maka Anda mungkin masih perlu membuka kotak sebelum Anda dapat mengetahui sesuatu yang spesifik tentang apa yang ada di dalam kotak, seperti apa jenisnya , atau apa nilai sebenarnya adalah.

Apa yang ada di dalam kotak?! Analogi

Bahkan setelah Anda membuka variabel, Anda masih seperti Brad Pitt di adegan terakhir di SE7EN ( peringatan : spoiler dan bahasa kasar dan kekerasan R-rated), karena bahkan setelah Anda membuka bungkusan saat ini, Anda berada dalam situasi berikut: Anda sekarang memiliki nil, atau kotak yang berisi sesuatu (tetapi Anda tidak tahu apa).

Anda mungkin tahu jenis sesuatu . Misalnya, jika Anda mendeklarasikan variabel sebagai tipe, [Int:Any?]maka Anda akan tahu bahwa Anda memiliki Kamus (berpotensi kosong) dengan subskrip integer yang menghasilkan konten terbungkus dari semua tipe lama.

Itu sebabnya berurusan dengan jenis koleksi (Kamus dan Array) di Swift bisa menjadi agak berbulu.

Inti masalah:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)
CommaToast
sumber
nice 😊 😊😊 😊 😊😊 😊 😊😊
SamSol
suka analoginya! Jadi, adakah cara yang lebih baik untuk membuka kotaknya? dalam contoh kode Anda
David T.
Kucing SchrΓΆdinger ada di dalam kotak
Jacksonkr
Terima kasih telah membingungkan saya .... Programmer macam apa yang memberikan Hadiah Ulang Tahun yang kosong? agak berarti
delta2flat
@Jacksonkr Atau tidak.
amsmath
13

Swift menempatkan premi tinggi pada keamanan jenis. Seluruh bahasa Swift dirancang dengan mempertimbangkan keselamatan. Ini adalah salah satu ciri khas Swift dan yang harus Anda sambut dengan tangan terbuka. Ini akan membantu dalam pengembangan kode yang bersih dan mudah dibaca serta membantu menjaga aplikasi Anda agar tidak macet.

Semua opsional di Swift ditandai dengan ?simbol. Dengan mengatur ?setelah nama tipe yang Anda deklarasikan sebagai opsional, Anda pada dasarnya casting ini bukan sebagai tipe yang sebelum ?, tetapi sebagai tipe opsional .


Catatan: Sebuah variabel atau jenis Intadalah tidak sama dengan Int?. Mereka adalah dua jenis berbeda yang tidak dapat dioperasikan satu sama lain.


Menggunakan Opsional

var myString: String?

myString = "foobar"

Ini tidak berarti bahwa Anda bekerja dengan tipe String. Ini berarti bahwa Anda bekerja dengan jenis String?(String Opsional, atau Opsional String). Bahkan, kapan pun Anda berusaha

print(myString)

saat runtime, konsol debug akan dicetak Optional("foobar"). Bagian " Optional()" menunjukkan bahwa variabel ini mungkin atau mungkin tidak memiliki nilai saat runtime, tetapi kebetulan saat ini mengandung string "foobar". Optional()Indikasi ini " " akan tetap kecuali Anda melakukan apa yang disebut "membuka" nilai opsional.

Membuka gulungan suatu opsi berarti bahwa Anda sekarang menggunakan tipe itu sebagai non-opsional. Ini akan menghasilkan tipe baru dan menetapkan nilai yang berada di dalam opsional itu ke jenis non-opsional baru. Dengan cara ini Anda dapat melakukan operasi pada variabel itu karena telah dijamin oleh kompiler untuk memiliki nilai yang solid.


Dibuka dengan syarat akan memeriksa apakah nilai dalam opsional nilatau tidak. Jika tidak nil, akan ada variabel konstan yang baru dibuat yang akan diberi nilai dan dibuka ke konstanta non-opsional. Dan dari sana Anda dapat dengan aman menggunakan non-opsional di ifblok.

Catatan: Anda dapat memberikan konstanta membuka bungkus yang kondisional Anda dengan nama yang sama dengan variabel opsional yang Anda buka bungkus.

if let myString = myString {
    print(myString)
    // will print "foobar"
}

Opsional yang terbuka secara kondisional adalah cara terbersih untuk mengakses nilai opsional karena jika mengandung nilai nil maka semua yang ada di dalam blok if let tidak akan dieksekusi. Tentu saja, seperti pernyataan if apa pun, Anda dapat menyertakan blok lain

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

Membuka paksa secara paksa dilakukan dengan menggunakan apa yang dikenal sebagai !operator ("bang"). Ini kurang aman tetapi masih memungkinkan kode Anda untuk dikompilasi. Namun setiap kali Anda menggunakan operator bang Anda harus yakin 1000% bahwa variabel Anda sebenarnya mengandung nilai yang solid sebelum membuka paksa.

var myString: String?

myString = "foobar"

print(myString!)

Ini di atas sepenuhnya kode Swift yang valid. Mencetak nilaimyString yang ditetapkan sebagai "foobar". Pengguna akan melihat foobartercetak di konsol dan hanya itu saja. Tapi mari kita asumsikan nilainya tidak pernah ditetapkan:

var myString: String?

print(myString!) 

Sekarang kami memiliki situasi yang berbeda di tangan kami. Tidak seperti Objective-C, setiap kali ada upaya untuk membuka paksa opsional, dan opsional belum diatur dan nil, ketika Anda mencoba untuk membuka opsional untuk melihat apa yang ada di dalam aplikasi Anda akan crash.


Buka gulungan dg Tipe Casting . Seperti yang telah kami katakan sebelumnya bahwa meskipun Anda adalah unwrappingopsional, Anda benar-benar melakukan casting ke tipe non-opsional, Anda juga dapat melemparkan non-opsional ke jenis yang berbeda. Sebagai contoh:

var something: Any?

Di suatu tempat dalam kode kita, variabel somethingakan diatur dengan beberapa nilai. Mungkin kita menggunakan obat generik atau mungkin ada logika lain yang terjadi yang akan menyebabkan ini berubah. Jadi nanti dalam kode kita, kita ingin menggunakan somethingtetapi masih dapat memperlakukannya secara berbeda jika itu adalah jenis yang berbeda. Dalam hal ini Anda ingin menggunakanas kata kunci untuk menentukan ini:

Catatan: asOperator adalah cara Anda mengetik gips dalam Swift.

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

Perhatikan perbedaan antara kedua askata kunci tersebut. Seperti sebelumnya ketika kami secara paksa membuka bungkusan opsional, kami menggunakan !operator letusan untuk melakukannya. Di sini Anda akan melakukan hal yang sama tetapi alih-alih melakukan casting sebagai non-opsional, Anda juga melakukan casting Int. Dan itu harus dapat dibatalkan karena Int, jika tidak, seperti menggunakan operator bang ketika nilainya nilaplikasi Anda akan crash.

Dan untuk menggunakan variabel-variabel ini sama sekali dalam semacam operasi matematika mereka harus dibuka untuk melakukannya.

Sebagai contoh, dalam Swift hanya tipe data angka yang valid dari jenis yang sama dapat dioperasikan satu sama lain. Ketika Anda melemparkan sebuah tipe dengan as!Anda memaksa downcast variabel itu seolah-olah Anda yakin itu adalah tipe itu, karena itu aman untuk beroperasi dan tidak crash aplikasi Anda. Ini boleh saja asalkan variabelnya memang dari tipe yang Anda gunakan, jika tidak, Anda akan memiliki kekacauan di tangan Anda.

Meskipun demikian casting dengan as!akan memungkinkan kode Anda untuk dikompilasi. Dengan casting dengan as?cerita yang berbeda. Bahkan, as?nyatakan bahwa Anda Intadalah tipe data yang sama sekali berbeda.

Sekarang sudah Optional(0)

Dan jika Anda pernah mencoba mengerjakan pekerjaan rumah Anda menulis sesuatu seperti

1 + Optional(1) = 2

Guru matematika Anda kemungkinan memberi Anda "F". Sama dengan Swift. Kecuali Swift lebih suka tidak mengkompilasi sama sekali daripada memberi Anda nilai. Karena pada akhirnya hari opsional mungkin sebenarnya nol .

Safety First Kids.

Brandon A
sumber
Penjelasan yang bagus tentang tipe opsional. Membantu menjernihkan semuanya untuk saya.
Tobe_Sta
0

' berarti ekspresi rantai opsional

'berarti' adalah nilai gaya
masukkan deskripsi gambar di sini

gkgy
sumber