Apa artinya "membuka contoh"? Mengapa itu perlu?
Sejauh yang saya bisa berolahraga (ini juga sangat baru bagi saya) ...
Istilah "terbungkus" menyiratkan kita harus menganggap variabel opsional sebagai hadiah, terbungkus kertas mengkilap, yang mungkin (sayangnya!) Kosong .
Ketika "dibungkus", nilai variabel Opsional adalah enum dengan dua nilai yang mungkin (sedikit seperti Boolean). Enum ini menjelaskan apakah variabel tersebut memiliki nilai ( Some(T)
), atau tidak ( None
).
Jika ada nilai, ini dapat diperoleh dengan "membuka" variabel (memperoleh T
dari Some(T)
).
Bagaimana john!.apartment = number73
bedanya john.apartment = number73
? (Diparafrasekan)
Jika Anda menulis nama variabel opsional (misalnya teks john
, tanpa !
), ini merujuk pada enum "terbungkus" (Beberapa / Tidak Ada), bukan nilai itu sendiri (T). Jadi john
bukan turunan dari Person
, dan tidak memiliki apartment
anggota:
john.apartment
// 'Person?' does not have a member named 'apartment'
Nilai aktual Person
dapat dibuka dengan berbagai cara:
- "terpaksa membuka bungkus":
john!
(memberikan Person
nilai jika ada, kesalahan runtime jika nihil)
- "binding opsional":
if let p = john { println(p) }
(mengeksekusi println
jika nilai ada)
- "chaining opsional":
john?.learnAboutSwift()
(menjalankan metode ini jika nilainya ada)
Saya kira Anda memilih salah satu dari cara ini untuk membuka, tergantung pada apa yang harus terjadi dalam kasus nol, dan seberapa besar kemungkinannya. Desain bahasa ini memaksa case nil untuk ditangani secara eksplisit, yang saya kira meningkatkan keamanan dibandingkan Obj-C (di mana mudah untuk melupakan untuk menangani case nil).
Perbarui :
Tanda seru juga digunakan dalam sintaksis untuk mendeklarasikan "Opsional Ops Unwrapped".
Dalam contoh-contoh sejauh ini, john
variabel telah dinyatakan sebagai var john:Person?
, dan itu adalah Opsional. Jika Anda ingin nilai aktual dari variabel itu, Anda harus membuka bungkusnya, menggunakan salah satu dari tiga metode di atas.
Jika dinyatakan sebagai var john:Person!
gantinya, variabel akan menjadi Opsional Unwrapped Optional (lihat bagian dengan tajuk ini di buku Apple). Tidak perlu membuka variabel seperti ini saat mengakses nilai, dan john
dapat digunakan tanpa sintaks tambahan. Tetapi buku Apple mengatakan:
Opsinya yang terbuka secara implisit tidak boleh digunakan ketika ada kemungkinan variabel menjadi nol pada titik berikutnya. Selalu gunakan tipe opsional normal jika Anda perlu memeriksa nilai nol selama masa pakai variabel.
Pembaruan 2 :
Artikel " Fitur Swift Menarik " oleh Mike Ash memberikan beberapa motivasi untuk tipe opsional. Saya pikir ini tulisan yang bagus dan jelas.
Pembaruan 3 :
Artikel lain yang bermanfaat tentang penggunaan opsional yang terbuka untuk tanda seru: " Swift and the Last Mile " oleh Chris Adamson. Artikel ini menjelaskan bahwa ini adalah ukuran pragmatis oleh Apple yang digunakan untuk mendeklarasikan jenis yang digunakan oleh kerangka kerja Objective-C mereka yang mungkin mengandung nol. Mendeklarasikan suatu tipe sebagai opsional (menggunakan ?
) atau secara implisit membuka (menggunakan !
) adalah "pertukaran antara keselamatan dan kenyamanan". Dalam contoh-contoh yang diberikan dalam artikel, Apple telah memilih untuk menyatakan jenisnya secara implisit terbuka, membuat kode panggilan lebih nyaman, tetapi kurang aman.
Mungkin Apple mungkin menyisir kerangka kerja mereka di masa depan, menghilangkan ketidakpastian dari parameter yang secara implisit membuka ("mungkin tidak pernah nihil") dan menggantinya dengan opsional ("tentu saja bisa nihil dalam keadaan [mudah-mudahan, didokumentasikan!]") Atau standar non deklarasi-opsional ("tidak pernah nol"), berdasarkan perilaku yang tepat dari kode Objective-C mereka.
Inilah yang menurut saya bedanya:
Berarti john bisa nihil
Kompiler akan menafsirkan baris ini sebagai:
Sementara
Kompiler akan menafsirkan baris ini hanya sebagai:
Karena itu, gunakan! akan membuka bungkusan pernyataan if, dan membuatnya berjalan lebih cepat, tetapi jika john nil, maka kesalahan runtime akan terjadi.
Jadi membungkus di sini tidak berarti itu dibungkus memori, tetapi itu berarti itu dibungkus kode, dalam hal ini dibungkus dengan pernyataan if, dan karena Apple memperhatikan kinerja dalam runtime, mereka ingin memberi Anda cara untuk buat aplikasi Anda berjalan dengan kinerja terbaik.
Memperbarui:
Kembali ke jawaban ini setelah 4 tahun, karena saya mendapat reputasi tertinggi darinya di Stackoverflow :) Saya sedikit salah paham tentang makna membuka pada saat itu. Sekarang setelah 4 tahun saya percaya arti membuka di sini adalah untuk memperluas kode dari bentuk ringkas aslinya. Juga itu berarti menghilangkan ketidakjelasan di sekitar objek itu, karena kita tidak yakin dengan definisi itu nihil atau tidak. Sama seperti jawaban Ashley di atas, pikirkan itu sebagai hadiah yang tidak mengandung apa pun di dalamnya. Tapi saya masih berpikir bahwa membuka bungkus adalah kode membuka bungkus dan bukan membuka bungkus berdasarkan memori menggunakan enum.
sumber
TL; DR
Apa arti tanda seru dalam bahasa Swift?
Contoh
Sumber: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
sumber
let f: String! = "hello"
dan kemudianprint(f)
, hasilnyaOptional("hello")
bukan adil"hello"
.Jika john adalah var opsional (dideklarasikan demikian)
maka akan mungkin bagi john untuk tidak memiliki nilai (dalam bahasa ObjC, nilai nil)
Tanda seru pada dasarnya memberitahu kompiler "Saya tahu ini memiliki nilai, Anda tidak perlu mengujinya". Jika Anda tidak ingin menggunakannya, Anda dapat menguji kondisional untuk itu:
Bagian dalam ini hanya akan mengevaluasi jika john memiliki nilai.
sumber
“let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.”
Tapi itu berfungsi dengan baik tanpa! Sepertinya ada yang aneh di sini.John?
danJohn
ada dua tipe berbeda. Satu adalah tipe Person Opsional dan Tipe Person lainnya. Orang opsional perlu dibuka sebelum Anda bisa mengeluarkan orang tersebut. Tetapi seperti yang Anda katakan, ini tampaknya terjadi setidaknya dalam keadaan ini tanpa harus benar-benar melakukan apa pun. Ini sepertinya membuat! mubazir. Kecuali jika! adalah SELALU opsional tetapi hal yang disarankan untuk dilakukan untuk menangkap kesalahan waktu kompilasi. Jenis seperti memberikan vars / memungkinkan untuk jenis tertentu dapat secara eksplisitlet John: Person = ...
tetapi juga dapat disimpulkanlet John = ...
.Beberapa perspektif gambar besar untuk ditambahkan ke jawaban lain yang bermanfaat namun lebih detail-sentris:
Di Swift, tanda seru muncul dalam beberapa konteks:
let name = nameLabel!.text
var logo: UIImageView!
logo.image = thing as! UIImage
try! NSJSONSerialization.JSONObjectWithData(data, [])
Semua ini adalah konstruksi bahasa yang berbeda dengan makna yang berbeda, tetapi mereka semua memiliki tiga hal penting yang sama:
1. Poin seru menghindari pemeriksaan keamanan waktu kompilasi Swift.
Ketika Anda menggunakan
!
di Swift, Anda pada dasarnya mengatakan, "Hei, kompiler, saya tahu Anda pikir kesalahan bisa terjadi di sini, tapi saya tahu dengan pasti bahwa itu tidak akan pernah terjadi."Tidak semua kode yang valid cocok dengan kotak sistem tipe waktu kompilasi Swift - atau pemeriksaan tipe statis bahasa apa pun , dalam hal ini. Ada beberapa situasi di mana Anda dapat secara logis membuktikan bahwa kesalahan tidak akan pernah terjadi, tetapi Anda tidak dapat membuktikannya ke kompiler . Karena itulah para perancang Swift menambahkan fitur-fitur ini sejak awal.
Namun, kapan pun Anda menggunakannya
!
, Anda mengesampingkan memiliki jalur pemulihan untuk kesalahan, yang berarti bahwa ...2. Poin seru adalah potensi gangguan.
Tanda seru juga mengatakan, “Hei Swift, saya sangat yakin bahwa kesalahan ini tidak pernah terjadi sehingga lebih baik bagi Anda untuk menabrak seluruh aplikasi saya daripada bagi saya untuk membuat kode jalur pemulihan untuk itu."
Itu pernyataan berbahaya. Itu bisa menjadi yang benar: dalam kode mission-critical di mana Anda telah berpikir keras tentang invarian kode Anda, mungkin output palsu lebih buruk daripada crash.
Namun, ketika saya melihat
!
di alam liar, jarang digunakan dengan penuh kesadaran. Sebagai gantinya, itu sering berarti, "nilai ini opsional dan saya tidak benar-benar berpikir terlalu keras tentang mengapa bisa nol atau bagaimana menangani dengan baik situasi itu, tetapi menambahkan!
membuatnya mengkompilasi ... jadi kode saya sudah benar, kan?"Waspadalah terhadap kesombongan titik seru. Sebagai gantinya…
3. Poin seru paling baik digunakan dengan hemat.
Setiap
!
konstruksi ini memiliki?
rekanan yang memaksa Anda untuk menangani kasus kesalahan / nol:if let name = nameLabel?.text { ... }
var logo: UIImageView?
logo.image = thing as? UIImage
try? NSJSONSerialization.JSONObjectWithData(data, [])
Jika Anda tergoda untuk menggunakannya
!
, selalu baik untuk mempertimbangkan dengan hati-hati mengapa Anda tidak menggunakannya?
. Apakah crash program Anda benar-benar pilihan terbaik jika!
operasi gagal? Mengapa nilai itu opsional / tersedia?Apakah ada jalur pemulihan yang masuk akal yang bisa diambil kode Anda dalam kasus nil / error? Jika demikian, kode itu.
Jika tidak mungkin nol, jika kesalahan tidak pernah terjadi, maka adakah cara yang masuk akal untuk mengolah logika Anda sehingga kompiler tahu itu? Jika demikian, lakukanlah; kode Anda akan lebih rentan terhadap kesalahan.
Ada saat-saat ketika tidak ada cara yang masuk akal untuk menangani kesalahan, dan mengabaikan kesalahan - dan dengan demikian melanjutkan dengan data yang salah - akan lebih buruk daripada menabrak. Itulah saat-saat untuk menggunakan kekuatan membuka bungkus.
Saya secara berkala mencari seluruh basis kode saya
!
dan mengaudit setiap penggunaannya. Sangat sedikit penggunaan yang digunakan untuk pengawasan. (Pada tulisan ini, seluruh kerangka Siesta memiliki tepat dua contohnya .)Itu tidak berarti Anda tidak boleh menggunakan
!
kode Anda - hanya saja Anda harus menggunakannya dengan penuh perhatian , dan jangan pernah menjadikannya pilihan default.sumber
func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) }
Mengingat 3. apakah ada cara yang lebih baik untuk menulisnya?func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
john
adalah opsionalvar
. Jadi bisa mengandungnil
nilai. Untuk memastikan bahwa nilainya tidak nol gunakan a!
di akhirvar
nama.Dari dokumentasi
“Setelah Anda yakin bahwa opsional mengandung nilai, Anda dapat mengakses nilai dasarnya dengan menambahkan tanda seru (!) Di akhir nama opsional. Tanda seru secara efektif mengatakan, “Saya tahu bahwa opsional ini pasti memiliki nilai; tolong gunakan itu. "
Cara lain untuk memeriksa nilai nihil adalah
sumber
john.apartment = number73
juga mengatakan "Saya tahu bahwa opsi ini pasti memiliki nilai; tolong gunakan." ...Berikut ini beberapa contohnya:
Di mana
word
nilai opsional. berarti mungkin atau mungkin tidak mengandung nilai.Di sini
name
memiliki nilai sehingga kami dapat menetapkannyaDi mana
dog
tidak dibuka secara paksa berarti itu harus mengandung nilaiAplikasi akan macet karena kami ditugaskan
nil
untuk membuka bungkussumber
var c:Int = nil
akan mendapatkan: "Nil tidak dapat menginisialisasi tipe yang ditentukan 'int'"Pada kasus ini...
var John: Orang!
itu berarti, bahwa pada awalnya John akan memiliki nilai nol, itu akan ditetapkan dan sekali ditetapkan tidak akan pernah dipimpin lagi. Karena itu untuk kenyamanan saya dapat menggunakan sintaks yang lebih mudah untuk mengakses var opsional karena ini adalah "opsional yang tidak dibungkus secara implisit"
sumber
Jika Anda berasal dari bahasa C-family, Anda akan berpikir "pointer ke objek tipe X yang mungkin menjadi alamat memori 0 (NULL)", dan jika Anda berasal dari bahasa yang diketik secara dinamis Anda akan menjadi berpikir "Objek yang mungkin tipe X tetapi mungkin tipe tidak terdefinisi". Tak satu pun dari ini yang benar, meskipun secara tidak langsung yang pertama dekat.
Cara Anda harus memikirkannya adalah seolah-olah benda itu seperti:
Ketika Anda menguji nilai opsional Anda dengan
foo == nil
itu benar-benar kembalifoo.isNil
, dan ketika Anda mengatakanfoo!
itu kembalifoo.realObject
dengan pernyataan itufoo.isNil == false
. Penting untuk dicatat ini karena jikafoo
sebenarnya nol ketika Anda melakukannyafoo!
, itu adalah kesalahan runtime, jadi biasanya Anda ingin menggunakan conditional let, kecuali jika Anda sangat yakin bahwa nilainya tidak akan menjadi nol. Tipuan semacam ini berarti bahwa bahasa tersebut dapat diketik dengan kuat tanpa memaksa Anda untuk menguji apakah nilainya ada di mana-mana.Dalam praktiknya, itu tidak benar-benar berperilaku seperti itu karena pekerjaan dilakukan oleh kompiler. Pada level tinggi ada tipe
Foo?
yang terpisahFoo
, dan yang mencegah funcs yang menerima tipeFoo
dari menerima nilai nol, tetapi pada level rendah nilai opsional bukanlah objek yang benar karena tidak memiliki properti atau metode; kemungkinan itu sebenarnya adalah pointer yang mungkin oleh NULL (0) dengan tes yang sesuai ketika membuka paksa.Ada situasi lain di mana Anda akan melihat tanda seru pada suatu jenis, seperti pada:
Ini kira-kira setara dengan menerima opsional dengan membuka paksa, yaitu:
Anda dapat menggunakan ini untuk memiliki metode yang secara teknis menerima nilai opsional tetapi akan memiliki kesalahan runtime jika nihil. Dalam versi Swift saat ini, ini tampaknya mem-bypass pernyataan is-not-nil sehingga Anda akan memiliki kesalahan tingkat rendah sebagai gantinya. Secara umum bukan ide yang baik, tetapi dapat berguna saat mengkonversi kode dari bahasa lain.
sumber
Itu! berarti bahwa Anda dipaksa membuka objek! mengikuti. Info lebih lanjut dapat ditemukan dalam dokumentasi Apel, yang dapat ditemukan di sini: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html
sumber
Jika Anda terbiasa dengan C #, ini seperti tipe Nullable yang juga dideklarasikan menggunakan tanda tanya:
Dan tanda seru dalam kasus ini setara dengan mengakses properti .Value dari tipe nullable seperti ini:
sumber
Dalam variabel C objektif tanpa nilai sama dengan 'nil' (juga dimungkinkan untuk menggunakan nilai 'nil' sama dengan 0 dan salah), maka dimungkinkan untuk menggunakan variabel dalam pernyataan bersyarat (Variabel yang memiliki nilai sama dengan 'BENAR' 'dan mereka yang tidak memiliki nilai sama dengan' SALAH ').
Swift memberikan keamanan jenis dengan memberikan 'nilai opsional'. yaitu mencegah kesalahan yang terbentuk dari menetapkan variabel dari jenis yang berbeda.
Jadi di Swift, hanya boolean yang dapat diberikan pada pernyataan bersyarat.
Di sini, meskipun 'hw' adalah string, itu tidak dapat digunakan dalam pernyataan if seperti dalam objektif C.
Untuk itu perlu dibuat sebagai,
sumber
Itu! pada akhir suatu objek mengatakan objek adalah opsional dan untuk membuka bungkus jika tidak dapat mengembalikan nol. Ini sering digunakan untuk menjebak kesalahan yang jika tidak akan crash program.
sumber
Singkatnya (!): Setelah Anda mendeklarasikan variabel dan Anda yakin variabel tersebut memegang nilai.
kalau tidak Anda harus melakukan ini pada setiap setelah melewati nilai ...
sumber
John adalah Person opsional, artinya dapat memiliki nilai atau nol.
digunakan jika john bukan opsional. Karena john tidak pernah nol, kita bisa yakin itu tidak akan memanggil apartemen dengan nilai nol. Sementara
menjanjikan kompiler bahwa john tidak nol lalu membuka opsi untuk mendapatkan nilai john dan mengakses properti apartemen john. Gunakan ini jika Anda tahu bahwa john tidak nol. Jika Anda menyebutnya sebagai opsi nihil, Anda akan mendapatkan kesalahan runtime.
Dokumentasi mencakup contoh yang bagus untuk menggunakan ini di mana dikonversiNumber adalah opsional.
sumber
john.apartment = number73
, itu TIDAK AKAN menghasilkan kesalahan kecuali jika saya menempatkan '!' setelah john?john
opsi memberitahu kompiler bahwa saya memerlukannya non-nihil? ... Dan dalamvar jack: Person = john!
contoh Anda , bukankah kekurangan? setelah Orang mengatakan kepada kompiler bahwa Anda harusjohn
non-nil? Secara keseluruhan,!
sepertinya sepertinya agak berlebihan ... Rasanya masih seperti kehilangan sesuatu pada bagian "mengapa" dari!
...Sederhananya, tanda seru berarti opsional sedang dibuka. Opsional adalah variabel yang dapat memiliki nilai atau tidak - jadi Anda dapat memeriksa apakah variabel tersebut kosong, menggunakan pernyataan if let seperti yang ditunjukkan di sini , dan kemudian paksakan dibuka dengan paksa. Jika Anda memaksa membuka opsi yang kosong, program Anda akan macet, jadi hati-hati! Opsional dinyatakan dengan meletakkan tanda tanya di akhir penugasan eksplisit ke variabel, misalnya saya bisa menulis:
Variabel ini tidak memiliki nilai. Jika saya membuka bungkusannya, program akan macet dan Xcode akan memberi tahu Anda Anda mencoba membuka bungkusan opsional dengan nilai nol.
Harapan itu membantu.
sumber
DALAM KATA-KATA SEDERHANA
MENGGUNAKAN tanda seru menunjukkan bahwa variabel harus terdiri dari nilai nihil (tidak boleh nol)
sumber
Seluruh cerita dimulai dengan fitur swift yang disebut opsional vars. Ini adalah vars yang mungkin memiliki nilai atau mungkin tidak memiliki nilai. Secara umum swift tidak memungkinkan kami untuk menggunakan variabel yang tidak diinisialisasi, karena ini dapat menyebabkan crash atau alasan yang tidak terduga dan juga server placeholder untuk backdoors. Jadi untuk mendeklarasikan variabel yang nilainya tidak ditentukan pada awalnya, kami menggunakan '?'. Ketika variabel tersebut dideklarasikan, untuk menggunakannya sebagai bagian dari ekspresi seseorang harus membukanya sebelum digunakan, membuka bungkus adalah operasi di mana nilai variabel ditemukan ini berlaku untuk objek. Tanpa membuka bungkus jika Anda mencoba menggunakannya, Anda akan memiliki kesalahan waktu kompilasi. Untuk membuka bungkusan variabel yang merupakan var opsional, tanda seru "!" digunakan.
Sekarang ada saatnya ketika Anda tahu bahwa variabel opsional tersebut akan diberi nilai oleh sistem misalnya atau program Anda sendiri tetapi beberapa waktu kemudian, misalnya gerai UI, dalam situasi seperti itu alih-alih mendeklarasikan variabel opsional menggunakan tanda tanya "?" kita gunakan "!".
Dengan demikian sistem mengetahui bahwa variabel ini yang dideklarasikan dengan "!" opsional sekarang dan tidak memiliki nilai tetapi akan menerima nilai di kemudian hari.
Dengan demikian tanda seru memiliki dua penggunaan yang berbeda, 1. Untuk mendeklarasikan variabel yang akan opsional dan akan menerima nilai dengan pasti nanti 2. Untuk membuka bungkus variabel opsional sebelum menggunakannya dalam ekspresi.
Deskripsi di atas menghindari terlalu banyak hal teknis, saya harap.
sumber
Jika Anda menggunakannya sebagai opsional, itu membuka bungkus opsional dan melihat apakah ada sesuatu di sana. Jika Anda menggunakannya dalam pernyataan if-else adalah kode untuk TIDAK. Sebagai contoh,
sumber
Variabel opsional dapat berisi nilai atau mungkin tidak
kasus 1:
var myVar:String? = "Something"
kasus 2:
var myVar:String? = nil
sekarang jika Anda bertanya kepada myVar !, Anda memberi tahu kompiler untuk mengembalikan nilai dalam kasus 1, itu akan kembali
"Something"
dalam kasus 2 itu akan crash.
Berarti ! tanda akan memaksa kompiler untuk mengembalikan nilai, bahkan jika itu tidak ada. Itulah sebabnya nama Force Unwrapping .
sumber
sumber
BERTANYA PADA DIRI SENDIRI
person?
memilikiapartment
anggota / properti? ATAUperson
memilikiapartment
anggota / properti?Jika Anda tidak dapat menjawab pertanyaan ini, maka lanjutkan membaca:
Untuk memahami Anda mungkin perlu tingkat super-dasar pemahaman Generik . Lihat di sini . Banyak hal di Swift ditulis menggunakan Generics. Opsional disertakan
Kode di bawah ini telah tersedia dari video Stanford ini . Sangat disarankan Anda untuk menonton 5 menit pertama
Opsional adalah enum dengan hanya 2 kasus
Ikatan opsional:
ketika Anda mengatakan
var john: Person?
Anda benar-benar berarti:Apakah enum di atas memiliki properti yang bernama
apartment
? Apakah Anda melihatnya di mana saja? Ini tidak ada sama sekali! Namun jika Anda membuka bungkusnya maka lakukanperson!
... Anda bisa ... apa yang dilakukannya di bawah tenda adalah:Optional<Person>.Some(Person(name: "John Appleseed"))
Seandainya Anda mendefinisikan
var john: Person
alih-alih:var john: Person?
maka Anda tidak perlu lagi!
menggunakan, karenaPerson
itu sendiri memiliki anggotaapartment
Sebagai diskusi di masa depan tentang mengapa menggunakan
!
untuk membuka bungkus kadang-kadang tidak disarankan lihat Tanya Jawab inisumber