Apa arti tanda seru dalam bahasa Swift?


518

Panduan Bahasa Pemrograman Swift memiliki contoh berikut:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

Kemudian ketika menugaskan apartemen kepada orang tersebut, mereka menggunakan tanda seru untuk "membuka contoh":

john!.apartment = number73

Apa artinya "membuka contoh"? Mengapa itu perlu? Apa bedanya dengan melakukan hal berikut:

john.apartment = number73

Saya sangat baru dalam bahasa Swift. Hanya mencoba mendapatkan dasar-dasarnya.


UPDATE:
Bagian besar dari teka-teki yang saya lewatkan (tidak secara langsung dinyatakan dalam jawaban - setidaknya tidak pada saat penulisan ini) adalah ketika Anda melakukan hal berikut:

var john: Person?

itu TIDAK berarti bahwa " johnadalah tipe Persondan mungkin nihil", seperti yang saya pikir semula. Saya hanya salah paham Persondan Person?tipe yang sepenuhnya terpisah. Begitu saya memahami itu, semua yang lain ?, !kegilaan, dan jawaban-jawaban hebat di bawah ini, jauh lebih masuk akal.

Jawaban:


530

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 Tdari Some(T)).

Bagaimana john!.apartment = number73bedanya 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 johnbukan turunan dari Person, dan tidak memiliki apartmentanggota:

john.apartment
// 'Person?' does not have a member named 'apartment'

Nilai aktual Persondapat dibuka dengan berbagai cara:

  • "terpaksa membuka bungkus": john!(memberikan Personnilai jika ada, kesalahan runtime jika nihil)
  • "binding opsional": if let p = john { println(p) }(mengeksekusi printlnjika 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, johnvariabel 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 johndapat 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.


Saya tidak yakin dengan penjelasan ini. Jika Anda hanya menjalankan kode tanpa! masih mengembalikan nilai aktual. Mungkin itu! untuk kecepatan?
Richard Washington

BAIK. Jadi dokumen berbicara tentang penggunaan! ketika Anda tahu pasti itu bisa dibuka. Tetapi Anda dapat menjalankan kode baik-baik saja tanpa itu (opsi maju untuk daftar Anda - membuka secara implisit) DAN tanpa memeriksa terlebih dahulu. Anda mendapatkan kembali nilainya atau nihil jika nihil. Tetapi jika Anda tahu pasti bahwa itu tidak nol maka gunakan! ...... tapi saya masih tidak bisa melihat mengapa Anda melakukan ini?
Richard Washington

2
Hai @RichardWashington - Saya telah menambahkan pembaruan untuk jawaban saya yang semoga menjelaskan beberapa hal ini.
Ashley

Pembaruan 3 adalah persis apa yang saya cari. Sebelum membacanya saya pikir Apple berbohong ketika mereka mengembalikan sesuatu seperti NSURLCredential! yang sebenarnya bisa nol.
Golden Thumb

Terima kasih atas jawaban fantastis @Ashley. Setelah membaca jawaban ini dan beberapa contoh kode cepat Apple, bagi saya tampaknya benar-benar dalam pertukaran antara keselamatan dan produktivitas, biasanya yang terbaik adalah berada di sisi yang lebih aman. Pengecualian penting yang saya temukan adalah ketika Apple menggunakan pembatalan paksa untuk elemen UI, yang masuk akal karena pertama, elemen UI biasanya opsional ketika melibatkan storyboard (karena mereka tidak diberi nilai secara terprogram), dan kedua karena mereka hampir tidak pernah nil kecuali pengontrol tampilan yang mengandungnya adalah nil, dalam hal ini diskusi ini jika diperdebatkan.
Mihir

127

Inilah yang menurut saya bedanya:

var john: Person?

Berarti john bisa nihil

john?.apartment = number73

Kompiler akan menafsirkan baris ini sebagai:

if john != nil {
    john.apartment = number73
}

Sementara

john!.apartment = number73

Kompiler akan menafsirkan baris ini hanya sebagai:

john.apartment = number73

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.


9
Di taman bermain saya john.apartment = number73 tidak dapat dikompilasi, Anda harus menentukan john? .Apartment = number73
Chuck Pinkert

2
@ ChuckPinkert benar, baris ke-4 harus diedit ke john? .Apartment = number73, jawaban yang bagus!
Bruce

john.apartment = number73 memberikan kesalahan: nilai tipe Opsional 'Orang?' tidak terbuka: maksud Anda menggunakan '!' atau '?'?
spiderman

4
Membuka membuka tidak ada hubungannya dengan kinerja wrt. The "nil check" masih harus dilakukan pada saat runtime, satu-satunya perbedaan adalah bahwa kesalahan runtime akan dilemparkan seandainya tidak ada nilai hadir dalam Opsional.
madidier

67

TL; DR

Apa arti tanda seru dalam bahasa Swift?

Tanda seru secara efektif mengatakan, “Saya tahu bahwa opsional ini pasti memiliki nilai; tolong gunakan itu. " Ini dikenal sebagai pembongkaran paksa nilai opsional:

Contoh

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

Sumber: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399


11
Jawaban Anda bagus karena saya mengerti apa yang sedang terjadi sekarang. Apa yang saya tidak mengerti adalah mengapa ada opsi tersirat secara implisit terbuka. Mengapa membuat sesuatu yang didefinisikan sebagai String opsional yang secara implisit tidak dibuka, daripada tipe String biasa? Penggunaannya setelahnya adalah sama. Apa yang saya lewatkan?
pachun

Ini sepertinya tidak benar lagi. Dalam Swift 5 repl, jika saya lakukan let f: String! = "hello"dan kemudian print(f), hasilnya Optional("hello")bukan adil "hello".
numbermaniac

37

Jika john adalah var opsional (dideklarasikan demikian)

var john: Person?

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:

if let otherPerson = john {
    otherPerson.apartment = number73
}

Bagian dalam ini hanya akan mengevaluasi jika john memiliki nilai.


4
Terima kasih atas tanggapannya, saya sekarang mengerti apa yang dikatakan tanda seru, saya masih mencoba membungkus kepala saya mengapa ... Anda mengatakan itu memberitahu kompiler "Saya tahu ini memiliki nilai, Anda tidak perlu menguji untuk Itu". Apa yang saya beli ketika kompiler tidak mengujinya? Tanpa tanda seru, apakah kompiler akan melakukan kesalahan (saya belum memiliki lingkungan, di mana saya dapat menguji Swift)? Adalah ! 100% diperlukan untuk semua vars opsional? Jika demikian, mengapa Apple repot-repot dengan itu, bukan hanya membuatnya jadi kurang ! berarti "Saya tahu ini memiliki nilai, Anda tidak perlu mengujinya"?
Troy

"Apakah kompiler akan melakukan kesalahan" - tidak, ia tetap berfungsi dengan baik mengembalikan nilai seperti yang diharapkan. Saya tidak mengerti. Adalah ! hanya untuk kecepatan ketika Anda yakin mungkin?
Richard Washington

Bahkan kemudian pada dokumen berbicara tentang opsional yang tidak dibungkus secara implisit menggunakan contoh di bawah ini: “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.
Richard Washington

3
@RichardWashington Anda bingung karena alasan yang bagus! Contoh-contoh dalam dokumen meninggalkan beberapa hal, dan menyesatkan karena semua yang mereka lakukan adalah menggunakan println. Rupanya, setidaknya ada beberapa contoh di mana membuka bungkus opsional tidak diperlukan. Salah satunya adalah saat menggunakan println. Lain adalah ketika menggunakan interpolasi string. Jadi, mungkin println dan interpolasi string terbuka di bawah selimut? Mungkin seseorang memiliki lebih banyak wawasan tentang ini. Melihat definisi header Beta Xcode6 tidak mengungkapkan kepada saya apa pun tentang sihir ini.
mbeaty

Ya, saya mengerti John?dan Johnada 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 eksplisit let John: Person = ...tetapi juga dapat disimpulkan let John = ....
Richard Washington

27

Beberapa perspektif gambar besar untuk ditambahkan ke jawaban lain yang bermanfaat namun lebih detail-sentris:

Di Swift, tanda seru muncul dalam beberapa konteks:

  • Membuka paksa: let name = nameLabel!.text
  • Opsinya terbuka secara implisit: var logo: UIImageView!
  • Pengecoran paksa: logo.image = thing as! UIImage
  • Pengecualian yang tidak ditangani: 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:

  • Membuka bungkus bersyarat: if let name = nameLabel?.text { ... }
  • Opsional: var logo: UIImageView?
  • Gips bersyarat: logo.image = thing as? UIImage
  • Pengecualian nol kegagalan: 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.


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?
jenson-button-event

Anda bisa mengatakanfunc isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
rkgibson2

Tanda seru tidak menghalangi pemeriksaan keamanan apa pun. Anda mendapatkan jaminan kerusakan jika opsionalnya nol. Opsional selalu dicentang. Itu hanya cara yang sangat brutal untuk melakukan pemeriksaan keamanan.
gnasher729

@ gnasher729 Seperti yang dinyatakan oleh jawabannya, ia menghindari pemeriksaan keamanan waktu kompilasi demi kegagalan runtime yang aman .
Paul Cantrell

24

johnadalah opsional var. Jadi bisa mengandung nilnilai. Untuk memastikan bahwa nilainya tidak nol gunakan a !di akhir varnama.

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

    if let j = json {
        // do something with j
    }

1
Ya, saya telah melihat hal itu dalam dokumentasi, tetapi tampaknya masih tidak perlu ... karena, bagi saya, john.apartment = number73juga mengatakan "Saya tahu bahwa opsi ini pasti memiliki nilai; tolong gunakan." ...
Troy

6
Ya, tetapi intinya adalah bahwa secara default Swift mencoba menangkap kesalahan pada waktu kompilasi. Jika Anda tahu atau berpikir Anda tahu bahwa variabel ini tidak dapat berisi nihil, Anda dapat menghapus centang itu dengan menggunakan tanda seru.
lassej

16

Berikut ini beberapa contohnya:

var name:String = "Hello World"
var word:String?

Di mana wordnilai opsional. berarti mungkin atau mungkin tidak mengandung nilai.

word = name 

Di sini namememiliki nilai sehingga kami dapat menetapkannya

var cow:String = nil
var dog:String!

Di mana dogtidak dibuka secara paksa berarti itu harus mengandung nilai

dog = cow

Aplikasi akan macet karena kami ditugaskan niluntuk membuka bungkus


1
var c:Int = nilakan mendapatkan: "Nil tidak dapat menginisialisasi tipe yang ditentukan 'int'"
Asususer

15

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"


1
Terima kasih. Saya juga menemukan tautan berikut yang menjelaskan hal ini. Jenis ini disebut "opsi yang tidak terbuka secara implisit". stackoverflow.com/questions/24122601/…
Kaydell

5

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:

struct Optional<T> {
   var isNil:Boolean
   var realObject:T
}

Ketika Anda menguji nilai opsional Anda dengan foo == nilitu benar-benar kembali foo.isNil, dan ketika Anda mengatakan foo!itu kembali foo.realObjectdengan pernyataan itu foo.isNil == false. Penting untuk dicatat ini karena jika foosebenarnya 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 terpisah Foo, dan yang mencegah funcs yang menerima tipe Foodari 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:

func foo(bar: String!) {
    print(bar)
}

Ini kira-kira setara dengan menerima opsional dengan membuka paksa, yaitu:

func foo(bar: String?) {
    print(bar!)
}

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.



3

Jika Anda terbiasa dengan C #, ini seperti tipe Nullable yang juga dideklarasikan menggunakan tanda tanya:

Person? thisPerson;

Dan tanda seru dalam kasus ini setara dengan mengakses properti .Value dari tipe nullable seperti ini:

thisPerson.Value

2

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.

var hw = "Hello World"

Di sini, meskipun 'hw' adalah string, itu tidak dapat digunakan dalam pernyataan if seperti dalam objektif C.

//This is an error

if hw

 {..}

Untuk itu perlu dibuat sebagai,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}

2

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.


2

Singkatnya (!): Setelah Anda mendeklarasikan variabel dan Anda yakin variabel tersebut memegang nilai.

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

kalau tidak Anda harus melakukan ini pada setiap setelah melewati nilai ...

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark

1

John adalah Person opsional, artinya dapat memiliki nilai atau nol.

john.apartment = number73

digunakan jika john bukan opsional. Karena john tidak pernah nol, kita bisa yakin itu tidak akan memanggil apartemen dengan nilai nol. Sementara

john!.apartment = number73

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.

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

Apakah Anda mengatakan bahwa jika John adalah nol, dan saya pergi john.apartment = number73, itu TIDAK AKAN menghasilkan kesalahan kecuali jika saya menempatkan '!' setelah john?
Troy

Jika john adalah opsional, Anda harus menggunakan tanda seru untuk mengakses propertinya karena kompiler harus tahu bahwa itu bukan nol. Jika ini bukan opsional, Anda tidak dapat menggunakan tanda seru.
Connor

1
Jika saya harus menggunakan tanda seru untuk SEMUA vars opsional, lalu mengapa Apple bahkan repot-repot dengan itu, sebagai lawan membuatnya sehingga kurangnya tanda seru berarti hal yang sama? Sepertinya mengasapi yang tidak perlu ... Atau adakah kasus yang masuk akal untuk TIDAK menggunakan tanda seru dengan var opsional?
Troy

Anda menggunakan tanda seru saat Anda membutuhkan opsional agar tidak nol. Seperti dalam kasus mengakses properti john. Anda bisa melakukan sesuatu seperti var jack: Orang? = john dimana jack juga merupakan jack opsional atau var: Orang = john! dimana jack adalah Person dan bukan nihil
Connor

Benar tetapi, dalam contoh asli, bukankah fakta bahwa saya mendereferensi johnopsi memberitahu kompiler bahwa saya memerlukannya non-nihil? ... Dan dalam var jack: Person = john!contoh Anda , bukankah kekurangan? setelah Orang mengatakan kepada kompiler bahwa Anda harus johnnon-nil? Secara keseluruhan, !sepertinya sepertinya agak berlebihan ... Rasanya masih seperti kehilangan sesuatu pada bagian "mengapa" dari !...
Troy

1

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:

var optionalExample: String?

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.


1

DALAM KATA-KATA SEDERHANA

MENGGUNAKAN tanda seru menunjukkan bahwa variabel harus terdiri dari nilai nihil (tidak boleh nol)


1

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.


1

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,

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)

1

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 .


@Moritz. Saya tidak tahu begitu? Tidak bisakah saya menjawab jika pertanyaan sudah dijawab? Apakah saya menyebabkan masalah atau ada yang salah dalam jawaban saya? Saya tidak mengerti. Mengapa Anda menolaknya untuk menjawabnya dengan benar? Saya mencoba menjawab berdasarkan pemahaman saya tentang konsep itu. Saya pikir beberapa orang akan merasa terbantu dengan penjelasan yang jelas dan mudah.
Ashish Pisey

@ Moritz Sekarang ini konstruktif. Anda seharusnya memberi tahu saya koreksi ini sejak awal, jika ini alasannya. Terima kasih untuk umpan baliknya. saya telah memperbaikinya.
Ashish Pisey

0
Simple the Optional variable allows nil to be stored.

var str : String? = nil

str = "Data"

To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"

func get(message : String){
   return
}

get(message : str!)  // Unwapped to pass as String

1
Tolong jangan tulis jawaban yang tidak menambahkan apa pun yang belum dicakup oleh jawaban sebelumnya.
Dalija Prasnikar

-1

BERTANYA PADA DIRI SENDIRI

  • Apakah tipe person?memiliki apartmentanggota / properti? ATAU
  • Apakah tipe personmemiliki apartmentanggota / 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

enum Optional<T>{
    case None
    case Some(T)
}

let x: String? = nil //actually means:

let x = Optional<String>.None

let x :String? = "hello" //actually means:

let x = Optional<String>.Some("hello")

var y = x! // actually means:

switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

Ikatan opsional:

let x:String? = something
if let y = x {
    // do something with y
}
//Actually means:

switch x{
case .Some(let y): print)(y) // or whatever else you like using 
case .None: break
}

ketika Anda mengatakan var john: Person?Anda benar-benar berarti:

enum Optional<Person>{
case .None
case .Some(Person)
}

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 lakukan person!... Anda bisa ... apa yang dilakukannya di bawah tenda adalah:Optional<Person>.Some(Person(name: "John Appleseed"))


Seandainya Anda mendefinisikan var john: Personalih-alih: var john: Person?maka Anda tidak perlu lagi !menggunakan, karena Personitu sendiri memiliki anggotaapartment


Sebagai diskusi di masa depan tentang mengapa menggunakan !untuk membuka bungkus kadang-kadang tidak disarankan lihat Tanya Jawab ini


2
Menggunakan slide yang sebenarnya di atas mungkin merupakan pelanggaran hak cipta: "... Universitas Stanford mempertahankan hak cipta untuk semua konten dalam koleksi iTunes U kami." , dari http://itunes.stanford.edu . Mungkin lebih baik untuk menjawab, dengan kata-kata Anda sendiri, konten yang Anda pelajari dari kursus yang Anda anggap menjawab pertanyaan ini (dan bukannya menggunakan slide, lebih baik kutip bagian yang relevan dari mereka dalam bentuk kode, dengan referensi, secara alami).
dfri

2
Argumen Anda adalah argumentum ad populum . Bagaimanapun, saya tidak percaya Stanford akan datang ke sini dan menegakkan hak-hak DMCA , tetapi selalu lebih baik jika jawaban Anda berdasarkan kata-kata Anda sendiri berdasarkan sumber resmi / sah , dan bukan dalam bentuk menyalin sumber-sumber itu secara langsung seperti di atas; terutama ketika kita berbicara tentang slide berhak cipta: sekali lagi, lebih baik untuk mengutip konten slide dalam blok kode (gambar kode umumnya tidak boleh di SO!).
dfri

1
Meta posting yang saya tautkan hanya menggambarkan pendekatan SO: untuk ini: posting seperti ini secara alami tidak akan dihapus jika beberapa pengguna acak melaporkannya sebagai pelanggaran hak cipta: hanya jika misalnya perwakilan Stanford. harus datang ke sini dan karena pos yang akan dihapus akan SO perlu menegakkannya. Karenanya saya menulis, " Saya tidak percaya Stanford akan datang ke sini dan menegakkan hak DMCA, ..." . Maksud saya di atas adalah bahwa saya percaya ini mungkin merupakan pelanggaran hak cipta (yang saya yakini salah), tetapi tentu saja tidak ada yang akan menegakkan ini. ...
dfri

1
Namun selain itu, memposting pertanyaan atau jawaban yang berisi gambar kode tidak disarankan karena beberapa alasan. Untuk menyelesaikannya: Saya sudah mencoba menunjukkan dengan komentar ini bahwa saya percaya jawaban ini, dalam bentuknya saat ini, tidak sepenuhnya sebagus yang seharusnya, karena dua alasan utama (slide berhak cipta, gambar kode). Secara alami tidak seorang pun, termasuk saya, akan melakukan apa pun tentang hal ini, saya hanya menunjukkannya sebagai referensi kami di masa mendatang (atau mungkin mendorong Anda untuk mengedit posting ini dengan penawaran dengan blok kode sebagai gantinya). Setuju, iTunes U! :)
dfri

2
@dfri menghapus gambar
Sayang
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.