Apa yang dimaksud dengan "Kesalahan fatal: Nil yang tidak terduga ditemukan saat membuka bungkus nilai Opsional"?

Aug 24 2015

Program Swift saya mengalami crash EXC_BAD_INSTRUCTIONdan salah satu kesalahan serupa berikut ini. Apa arti kesalahan ini, dan bagaimana cara memperbaikinya?

Kesalahan fatal: Tanpa diduga ditemukan nol saat membuka bungkus nilai Opsional

atau

Kesalahan fatal: Tidak terduga ditemukan nol saat secara implisit membuka bungkus nilai Opsional


Postingan ini dimaksudkan untuk mengumpulkan jawaban atas masalah yang "tak terduga nihil", agar tidak tersebar dan sulit ditemukan. Silakan menambahkan jawaban Anda sendiri atau mengedit jawaban wiki yang ada.

Jawaban

685 Hamish Aug 24 2015 at 02:10

Jawaban ini adalah wiki komunitas . Jika Anda merasa ini bisa diperbaiki, silakan edit !

Latar Belakang: Apa itu Opsional?

Di Swift, Optional<Wrapped>adalah tipe opsi : ini dapat berisi nilai apa pun dari tipe asli ("Dibungkus"), atau tanpa nilai sama sekali (nilai khusus nil). Nilai opsional harus dibuka sebelum dapat digunakan.

Opsional adalah tipe generik , yang berarti Optional<Int>dan Optional<String>merupakan tipe yang berbeda - tipe di dalamnya <>disebut tipe Wrapped. Di bawah kap, Opsional adalah enum dengan dua casing: .some(Wrapped)dan .none, di mana .nonesetara dengan nil.

Opsional dapat dideklarasikan menggunakan tipe bernama Optional<T>, atau (paling umum) sebagai singkatan dengan ?sufiks.

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?  // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int>  // equivalent to `Int?`

anOptionalInt = nil // now this variable contains nil instead of an integer

Opsional adalah alat sederhana namun ampuh untuk mengekspresikan asumsi Anda saat menulis kode. Kompilator dapat menggunakan informasi ini untuk mencegah Anda melakukan kesalahan. Dari Bahasa Pemrograman Swift :

Swift adalah bahasa yang aman untuk tipe , yang berarti bahasanya membantu Anda memperjelas tentang jenis nilai yang dapat digunakan kode Anda. Jika bagian dari kode Anda memerlukan a String, type safety mencegah Anda meneruskannya secara tidak Intsengaja. Demikian pula, jenis keamanan mencegah Anda secara tidak sengaja meneruskan opsional Stringke bagian kode yang memerlukan non-opsional String. Keamanan jenis membantu Anda menangkap dan memperbaiki kesalahan sedini mungkin dalam proses pengembangan.

Beberapa bahasa pemrograman lain juga memiliki tipe opsi umum : misalnya, Maybe di Haskell, opsi di Rust, dan opsional di C ++ 17.

Dalam bahasa pemrograman tanpa jenis opsi, nilai "sentinel" tertentu sering digunakan untuk menunjukkan tidak adanya nilai yang valid. Di Objective-C, misalnya, nil( pointer nol ) mewakili ketiadaan suatu objek. Untuk tipe primitif seperti int, pointer null tidak dapat digunakan, jadi Anda memerlukan variabel terpisah (seperti value: Intdan isValid: Bool) atau nilai sentinel yang ditentukan (seperti -1atau INT_MIN). Pendekatan ini rawan kesalahan karena mudah lupa untuk memeriksa isValidatau memeriksa nilai sentinel. Selain itu, jika nilai tertentu dipilih sebagai sentinel, itu berarti nilai tersebut tidak dapat lagi diperlakukan sebagai nilai yang valid .

Tipe opsi seperti Swift Optionalmemecahkan masalah ini dengan memperkenalkan nilai khusus yang terpisah nil(sehingga Anda tidak perlu menentukan nilai sentinel), dan dengan memanfaatkan sistem tipe kuat sehingga kompiler dapat membantu Anda mengingat untuk memeriksa nil bila perlu.


Mengapa saya mendapatkan " kesalahan fatal: tidak terduga ditemukan nol saat membuka bungkus nilai Opsional "?

Dalam rangka untuk mengakses nilai opsional ini (jika memiliki satu sama sekali), Anda perlu membuka bungkusan itu. Nilai opsional dapat dibuka dengan aman atau dengan paksa. Jika Anda membuka paksa sebuah opsional, dan tidak memiliki nilai, program Anda akan macet dengan pesan di atas.

Xcode akan menunjukkan kerusakan dengan menyorot sebaris kode. Masalahnya terjadi di baris ini.

Kerusakan ini dapat terjadi dengan dua jenis buka paksa:

1. Pembongkaran Paksa Eksplisit

Ini dilakukan dengan !operator sebagai opsional. Sebagai contoh:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH

Kesalahan fatal: Tanpa diduga ditemukan nol saat membuka bungkus nilai Opsional

Seperti anOptionalStringyang nildi sini, Anda akan mendapatkan kecelakaan pada baris di mana Anda memaksa unwrap itu.

2. Opsional yang Tidak Dibungkus Secara Implisit

Ini didefinisikan dengan a !, bukan ?setelah tipe.

var optionalDouble: Double!   // this value is implicitly unwrapped wherever it's used

Opsional ini diasumsikan berisi nilai. Oleh karena itu, setiap kali Anda mengakses opsional yang tidak terbungkus secara implisit, secara otomatis akan dibuka paksa untuk Anda. Jika tidak mengandung nilai, itu akan macet.

print(optionalDouble) // <- CRASH

Kesalahan fatal: Tidak terduga ditemukan nol saat secara implisit membuka bungkus nilai Opsional

Untuk mengetahui variabel mana yang menyebabkan crash, Anda dapat menahan sambil mengklik untuk menampilkan definisi, di mana Anda mungkin menemukan tipe opsional.

IBOutlets, khususnya, biasanya merupakan opsional yang tidak terbungkus secara implisit. Ini karena xib atau storyboard Anda akan menghubungkan outlet pada waktu proses, setelah inisialisasi. Oleh karena itu, Anda harus memastikan bahwa Anda tidak mengakses outlet sebelum dimuat. Anda juga harus memeriksa apakah koneksi sudah benar di file storyboard / xib Anda, jika tidak, nilainya akan berada nilpada waktu proses, dan karena itu akan macet saat dibuka bungkusnya secara implisit . Saat memperbaiki koneksi, coba hapus baris kode yang menentukan outlet Anda, lalu sambungkan kembali.


Kapan saya harus membuka paksa Opsional?

Pembongkaran Paksa Eksplisit

Sebagai aturan umum, Anda tidak boleh secara eksplisit memaksa membuka bungkus opsional dengan !operator. Mungkin ada kasus di mana penggunaan !dapat diterima - tetapi Anda hanya boleh menggunakannya jika Anda 100% yakin bahwa opsional berisi nilai.

Meskipun mungkin ada saat di mana Anda dapat menggunakan paksa membuka bungkus, seperti yang Anda ketahui fakta bahwa opsional berisi nilai - tidak ada satu tempat pun di mana Anda tidak dapat dengan aman membuka bungkus opsional itu.

Opsional yang Tidak Dibungkus Secara Implisit

Variabel ini dirancang agar Anda dapat menunda tugasnya hingga nanti dalam kode Anda. Merupakan tanggung jawab Anda untuk memastikan bahwa mereka memiliki nilai sebelum Anda mengaksesnya. Namun, karena melibatkan force unrapping, mereka masih secara inheren tidak aman - karena mereka menganggap nilai Anda bukan nil, meskipun menetapkan nil valid.

Anda sebaiknya hanya menggunakan opsi yang tidak terbungkus secara implisit sebagai upaya terakhir . Jika Anda dapat menggunakan variabel malas , atau memberikan nilai default untuk variabel - Anda harus melakukannya daripada menggunakan opsional yang tidak terbungkus secara implisit.

Namun, ada beberapa skenario di mana opsional yang tidak terbungkus secara implisit bermanfaat , dan Anda masih dapat menggunakan berbagai cara untuk membuka bungkusnya dengan aman seperti yang tercantum di bawah ini - tetapi Anda harus selalu menggunakannya dengan hati-hati.


Bagaimana saya dapat menangani Opsional dengan aman?

Cara termudah untuk memeriksa apakah suatu opsional berisi nilai, adalah membandingkannya dengan nil.

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesn’t contain a value.")
}

Namun, 99,9% dari waktu saat bekerja dengan opsional, Anda sebenarnya ingin mengakses nilai yang dikandungnya, jika mengandung satu sama sekali. Untuk melakukan ini, Anda dapat menggunakan Penjilidan Opsional .

Binding opsional

Pengikatan Opsional memungkinkan Anda untuk memeriksa apakah sebuah opsional berisi nilai - dan memungkinkan Anda untuk menetapkan nilai yang tidak dibungkus ke variabel atau konstanta baru. Ini menggunakan sintaks if let x = anOptional {...}atau if var x = anOptional {...}, tergantung apakah Anda perlu mengubah nilai variabel baru setelah mengikatnya.

Sebagai contoh:

if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t contain a number")
}

Apa yang dilakukan ini adalah pertama-tama memeriksa apakah opsional berisi nilai. Jika tidak , maka 'terbuka' nilai ditugaskan untuk variabel baru ( number) - yang kemudian dapat dengan bebas menggunakan seolah-olah itu non-opsional. Jika opsional tidak berisi nilai, klausa else akan dipanggil, seperti yang Anda harapkan.

Apa yang menarik tentang penjilidan opsional, adalah Anda dapat membuka beberapa opsional pada saat yang bersamaan. Anda bisa memisahkan pernyataan dengan koma. Pernyataan itu akan berhasil jika semua opsional telah dibuka.

var anOptionalInt : Int?
var anOptionalString : String?

if let number = anOptionalInt, let text = anOptionalString {
    print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
    print("One or more of the optionals don’t contain a value")
}

Trik rapi lainnya adalah Anda juga dapat menggunakan koma untuk memeriksa kondisi tertentu pada nilai, setelah membukanya.

if let number = anOptionalInt, number > 0 {
    print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}

Satu-satunya tangkapan dengan menggunakan pengikatan opsional dalam pernyataan if, adalah bahwa Anda hanya dapat mengakses nilai yang tidak dibungkus dari dalam cakupan pernyataan. Jika Anda membutuhkan akses ke nilai dari luar cakupan pernyataan, Anda dapat menggunakan pernyataan penjaga .

Sebuah pernyataan penjaga memungkinkan Anda untuk menentukan kondisi untuk sukses - dan ruang lingkup saat hanya akan terus mengeksekusi jika kondisi yang terpenuhi. Mereka didefinisikan dengan sintaks guard condition else {...}.

Jadi, untuk menggunakannya dengan pengikatan opsional, Anda dapat melakukan ini:

guard let number = anOptionalInt else {
    return
}

(Perhatikan bahwa di dalam badan penjaga, Anda harus menggunakan salah satu pernyataan pengalihan kontrol untuk keluar dari cakupan kode yang saat ini dijalankan).

Jika anOptionalIntberisi nilai, itu akan dibuka dan ditetapkan ke numberkonstanta baru . Kode setelah penjaga kemudian akan terus dijalankan. Jika tidak mengandung nilai - penjaga akan mengeksekusi kode di dalam tanda kurung, yang akan menyebabkan transfer kontrol, sehingga kode segera setelahnya tidak akan dieksekusi.

Hal yang benar-benar rapi tentang pernyataan penjaga adalah nilai yang tidak dibungkus sekarang tersedia untuk digunakan dalam kode yang mengikuti pernyataan tersebut (seperti yang kita tahu bahwa kode masa depan hanya dapat dijalankan jika opsional memiliki nilai). Ini bagus untuk menghilangkan 'pyramids of doom' yang dibuat dengan membuat beberapa pernyataan if.

Sebagai contoh:

guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")

Penjaga juga mendukung trik rapi yang sama dengan yang didukung pernyataan if, seperti membuka bungkusan beberapa opsi secara bersamaan dan menggunakan whereklausa.

Apakah Anda menggunakan pernyataan if atau guard sepenuhnya bergantung pada apakah kode di masa mendatang memerlukan opsional untuk memuat nilai.

Operator Penggabungan Nihil

The Nil penggabungan Operator adalah versi singkat bagus dari operator kondisional terner , terutama dirancang untuk mengkonversi optionals untuk non-optionals. Ini memiliki sintaks a ?? b, di mana aadalah tipe opsional dan tipe byang sama dengan a(meskipun biasanya non-opsional).

Ini pada dasarnya memungkinkan Anda mengatakan "Jika aberisi nilai, buka bungkusnya. Jika tidak, maka kembalikan bsebagai gantinya ”. Misalnya, Anda bisa menggunakannya seperti ini:

let number = anOptionalInt ?? 0

Ini akan menentukan tipe numberkonstan Int, yang akan berisi nilai anOptionalInt, jika mengandung nilai, atau 0sebaliknya.

Ini hanya singkatan dari:

let number = anOptionalInt != nil ? anOptionalInt! : 0

Rantai Opsional

Anda dapat menggunakan Rangkaian Opsional untuk memanggil metode atau mengakses properti secara opsional. Ini hanya dilakukan dengan mencadangkan nama variabel dengan a ?saat menggunakannya.

Misalnya, kita memiliki variabel foo, dengan tipe Fooinstance opsional .

var foo : Foo?

Jika kami ingin memanggil metode fooyang tidak mengembalikan apa pun, kami cukup melakukan:

foo?.doSomethingInteresting()

Jika fooberisi nilai, metode ini akan dipanggil di atasnya. Jika tidak, tidak ada hal buruk yang akan terjadi - kode akan terus berjalan.

(Ini adalah perilaku yang mirip dengan mengirim pesan ke nildi Objective-C)

Oleh karena itu, ini juga dapat digunakan untuk menyetel properti serta metode panggilan. Sebagai contoh:

foo?.bar = Bar()

Sekali lagi, tidak ada yang buruk akan terjadi di sini jika fooadalah nil. Kode Anda akan terus berjalan.

Trik rapi lainnya yang memungkinkan Anda lakukan oleh rangkaian opsional adalah memeriksa apakah menyetel properti atau memanggil metode berhasil. Anda dapat melakukan ini dengan membandingkan nilai pengembalian dengan nil.

(Ini karena nilai opsional akan dikembalikan, Void?bukan Voidpada metode yang tidak mengembalikan apa pun)

Sebagai contoh:

if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasn’t set successfully")
}

Namun, hal-hal menjadi sedikit lebih rumit saat mencoba mengakses properti atau memanggil metode yang mengembalikan nilai. Karena foobersifat opsional, apa pun yang dikembalikan darinya juga akan menjadi opsional. Untuk mengatasinya, Anda bisa membuka bungkus opsional yang dikembalikan menggunakan salah satu metode di atas - atau membuka bungkusan fooitu sendiri sebelum mengakses metode atau memanggil metode yang mengembalikan nilai.

Juga, seperti namanya, Anda bisa 'merangkai' pernyataan ini menjadi satu. Ini berarti jika foomemiliki properti opsional baz, yang memiliki properti qux- Anda dapat menulis yang berikut ini:

let optionalQux = foo?.baz?.qux

Sekali lagi, karena foodan bazbersifat opsional, nilai yang dikembalikan dari quxakan selalu menjadi opsional terlepas dari apakah nilai quxitu sendiri opsional.

map dan flatMap

Fitur yang sering kurang digunakan dengan pilihan adalah kemampuan untuk menggunakan fungsi mapdan flatMap. Ini memungkinkan Anda untuk menerapkan transformasi non-opsional ke variabel opsional. Jika sebuah opsional memiliki nilai, Anda dapat menerapkan transformasi yang diberikan padanya. Jika tidak memiliki nilai, ia akan tetap ada nil.

Misalnya, Anda memiliki string opsional:

let anOptionalString:String?

Dengan menerapkan mapfungsi padanya - kita dapat menggunakan stringByAppendingStringfungsi tersebut untuk menggabungkannya ke string lain.

Karena stringByAppendingStringmengambil argumen string non-opsional, kami tidak dapat memasukkan string opsional kami secara langsung. Namun, dengan menggunakan map, kita dapat menggunakan allow stringByAppendingStringuntuk digunakan jika anOptionalStringmemiliki nilai.

Sebagai contoh:

var anOptionalString:String? = "bar"

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // Optional("foobar")

Namun, jika anOptionalStringtidak memiliki nilai, mapakan kembali nil. Sebagai contoh:

var anOptionalString:String?

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // nil

flatMapbekerja sama dengan map, kecuali ini memungkinkan Anda untuk mengembalikan opsional lain dari dalam badan closure. Ini berarti Anda dapat memasukkan opsional ke dalam proses yang memerlukan input non-opsional, tetapi dapat mengeluarkan opsional itu sendiri.

try!

Sistem penanganan kesalahan Swift dapat digunakan dengan aman dengan Do-Try-Catch :

do {
    let result = try someThrowingFunc() 
} catch {
    print(error)
}

Jika someThrowingFunc()melontarkan kesalahan, kesalahan tersebut akan ditangkap dengan aman di catchblok.

The errorkonstan Anda lihat dalam catchblok belum dinyatakan oleh kami - itu secara otomatis oleh catch.

Anda juga dapat menyatakan errordiri Anda sendiri, memiliki keuntungan karena dapat mentransmisikannya ke format yang berguna, misalnya:

do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}

Menggunakan trycara ini adalah cara yang tepat untuk mencoba, menangkap dan menangani kesalahan yang berasal dari fungsi melempar.

Ada juga try?yang menyerap kesalahan:

if let result = try? someThrowingFunc() {
    // cool
} else {
    // handle the failure, but there's no error information available
}

Tetapi sistem penanganan kesalahan Swift juga menyediakan cara untuk "mencoba paksa" dengan try!:

let result = try! someThrowingFunc()

Konsep yang dijelaskan dalam posting ini juga berlaku di sini: jika terjadi kesalahan, aplikasi akan macet.

Anda hanya boleh menggunakan try!jika Anda dapat membuktikan bahwa hasilnya tidak akan pernah gagal dalam konteks Anda - dan ini sangat jarang.

Sebagian besar waktu Anda akan menggunakan sistem Do-Try-Catch lengkap - dan yang opsional try?,, dalam kasus yang jarang terjadi di mana penanganan kesalahan tidak penting.


Sumber daya

65 Sajjon Dec 19 2016 at 20:26

TL; DR jawaban

Dengan sedikit pengecualian , aturan ini emas:

Hindari penggunaan !

Deklarasikan variabel opsional ( ?), bukan opsional yang dibuka secara implisit (IUO) ( !)

Dengan kata lain, lebih baik gunakan:
var nameOfDaughter: String?

Dari pada:
var nameOfDaughter: String!

Buka bungkus variabel opsional menggunakan if letatauguard let

Salah satu variabel yang dibuka seperti ini:

if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}

Atau seperti ini:

guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")

Jawaban ini dimaksudkan agar ringkas, untuk pemahaman penuh membaca jawaban yang diterima


Sumber daya

40 DuncanC Feb 28 2016 at 21:37

Pertanyaan ini muncul SEPANJANG WAKTU . Ini adalah salah satu hal pertama yang dihadapi oleh pengembang Swift baru.

Latar Belakang:

Swift menggunakan konsep "Opsional" untuk menangani nilai yang bisa berisi nilai, atau tidak. Dalam bahasa lain seperti C, Anda mungkin menyimpan nilai 0 dalam variabel untuk menunjukkan bahwa itu tidak mengandung nilai. Namun, bagaimana jika 0 adalah nilai yang valid? Kemudian Anda bisa menggunakan -1. Bagaimana jika -1 adalah nilai yang valid? Dan seterusnya.

Opsional Swift memungkinkan Anda menyiapkan variabel jenis apa pun agar berisi nilai yang valid, atau tanpa nilai.

Anda memberi tanda tanya setelah tipe ketika Anda mendeklarasikan variabel menjadi berarti (tipe x, atau tanpa nilai).

Opsional sebenarnya adalah wadah yang berisi variabel dari jenis tertentu, atau tidak sama sekali.

Opsional harus "dibuka" untuk mengambil nilai di dalamnya.

The "!" operator adalah operator "paksa membuka". Ia mengatakan "percayalah. Saya tahu apa yang saya lakukan. Saya jamin bahwa ketika kode ini berjalan, variabel tidak akan berisi nol." Jika Anda salah, Anda crash.

Kecuali jika Anda benar - benar tahu apa yang Anda lakukan, hindari tanda "!" paksa operator membuka bungkus. Ini mungkin sumber error terbesar untuk pemrogram Swift pemula.

Bagaimana menangani pilihan:

Ada banyak cara lain untuk menangani pilihan yang lebih aman. Berikut beberapa (bukan daftar lengkap)

Anda dapat menggunakan "pengikatan opsional" atau "jika biarkan" mengatakan "jika opsional ini berisi nilai, simpan nilai itu ke dalam variabel non-opsional baru. Jika opsional tidak berisi nilai, lewati isi pernyataan if ini ".

Berikut adalah contoh penjilidan opsional dengan fooopsional kami :

if let newFoo = foo //If let is called optional binding. {
  print("foo is not nil")
} else {
  print("foo is nil")
}

Perhatikan bahwa variabel yang Anda tentukan saat Anda menggunakan penawaran opsional hanya ada (hanya "dalam cakupan") di badan pernyataan if.

Alternatifnya, Anda dapat menggunakan pernyataan penjaga, yang memungkinkan Anda keluar dari fungsi jika variabelnya nil:

func aFunc(foo: Int?) {
  guard let newFoo = input else { return }
  //For the rest of the function newFoo is a non-optional var
}

Pernyataan penjaga ditambahkan di Swift 2. Penjaga memungkinkan Anda mempertahankan "jalur emas" melalui kode Anda, dan menghindari tingkat if bersarang yang terus meningkat yang terkadang dihasilkan dari penggunaan pengikatan opsional "jika biarkan".

Ada juga konstruksi yang disebut "operator penggabungan nihil". Ini mengambil bentuk "optional_var ?? replacement_val". Ini mengembalikan variabel non-opsional dengan jenis yang sama seperti data yang terdapat dalam opsional. Jika opsional berisi nil, ia mengembalikan nilai ekspresi setelah "??" simbol.

Jadi Anda bisa menggunakan kode seperti ini:

let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")

Anda juga dapat menggunakan try / catch atau guard error handling, tetapi umumnya salah satu teknik di atas lebih bersih.

EDIT:

Gotcha lain yang sedikit lebih halus dengan pilihan adalah "pilihan yang tidak terbungkus secara implisit. Saat mendeklarasikan foo, kita dapat mengatakan:

var foo: String!

Dalam hal ini foo masih opsional, tetapi Anda tidak perlu membukanya untuk mereferensikannya. Itu berarti setiap kali Anda mencoba mereferensikan foo, Anda macet jika nihil.

Jadi kode ini:

var foo: String!


let upperFoo = foo.capitalizedString

Akan macet saat merujuk ke properti capitalizedString foo meskipun kami tidak membuka paksa foo. cetakannya terlihat bagus, tapi sebenarnya tidak.

Jadi, Anda ingin sangat berhati-hati dengan opsi yang tidak terbungkus secara implisit. (dan mungkin bahkan menghindari mereka sepenuhnya sampai Anda memiliki pemahaman yang kuat tentang pilihan.)

Intinya: Saat Anda pertama kali mempelajari Swift, anggaplah "!" karakter bukan bagian dari bahasa. Kemungkinan akan membuat Anda mendapat masalah.

13 Tharzeez Nov 17 2017 at 23:15

Karena jawaban di atas dengan jelas menjelaskan cara bermain aman dengan Opsional. Saya akan mencoba menjelaskan apa itu Opsional sebenarnya dengan cepat.

Cara lain untuk mendeklarasikan variabel opsional adalah

var i : Optional<Int>

Dan tipe Opsional tidak lain adalah pencacahan dengan dua kasus, yaitu

 enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none 
    case some(Wrapped)
    .
    .
    .
}

Jadi untuk menetapkan nil ke variabel 'i' kami. Kita dapat melakukan var i = Optional<Int>.none atau memberikan nilai, kita akan memberikan beberapa nilai var i = Optional<Int>.some(28)

Menurut swift, 'nihil' adalah ketiadaan nilai. Dan untuk membuat instance yang diinisialisasi dengan nilKami harus mematuhi protokol yang disebut ExpressibleByNilLiteraldan hebat jika Anda dapat menebaknya, hanya Optionalsmenyesuaikan ExpressibleByNilLiteraldan menyesuaikan dengan jenis lain tidak disarankan.

ExpressibleByNilLiteralmemiliki satu metode yang disebut init(nilLiteral:)yang menginisialisasi sebuah instace dengan nil. Anda biasanya tidak akan memanggil metode ini dan menurut dokumentasi swift tidak disarankan untuk memanggil penginisialisasi ini secara langsung karena kompilator memanggilnya setiap kali Anda menginisialisasi tipe Opsional dengan nilliteral.

Bahkan diriku sendiri harus membungkus (tidak ada permainan kata-kata) kepalaku di sekitar Opsional: D Selamat Bertukar Semua .

12 QiunCheng Jul 30 2016 at 14:18

Pertama, Anda harus tahu apa itu nilai Opsional. Anda dapat membuka Bahasa Pemrograman Swift untuk detailnya.

Kedua, Anda harus tahu bahwa nilai opsional memiliki dua status. Satu adalah nilai penuh, dan yang lainnya adalah nilai nihil. Jadi sebelum Anda menerapkan nilai opsional, Anda harus memeriksa statusnya.

Anda bisa menggunakan if let ...atau guard let ... elsedan sebagainya.

Dengan satu cara lain, jika Anda tidak ingin memeriksa status variabel sebelum menerapkan, Anda juga dapat menggunakan var buildingName = buildingName ?? "buildingName".

8 Wissa Apr 13 2018 at 21:06

Saya mengalami kesalahan ini sekali ketika saya mencoba untuk mengatur nilai Outlet saya dari metode persiapan untuk segue sebagai berikut:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? DestinationVC{

        if let item = sender as? DataItem{
            // This line pops up the error
            destination.nameLabel.text = item.name
        }
    }
}

Kemudian saya menemukan bahwa saya tidak dapat mengatur nilai-nilai outlet pengontrol tujuan karena pengontrol belum dimuat atau diinisialisasi.

Jadi saya menyelesaikannya dengan cara ini:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? DestinationVC{

        if let item = sender as? DataItem{
            // Created this method in the destination Controller to update its outlets after it's being initialized and loaded
            destination.updateView(itemData:  item)
        }
    }
}

Pengontrol Tujuan:

// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""

// Outlets
@IBOutlet weak var nameLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    nameLabel.text = name
}

func updateView(itemDate: ObjectModel) {
    name = itemDate.name
}

Saya harap jawaban ini membantu siapa pun di luar sana dengan masalah yang sama karena saya menemukan jawaban yang ditandai adalah sumber daya yang bagus untuk memahami pilihan dan cara kerjanya tetapi belum mengatasi masalah itu sendiri secara langsung.

7 Cristik Sep 19 2018 at 13:04

Pada dasarnya Anda mencoba menggunakan nilai nil di tempat di mana Swift hanya mengizinkan nilai non-nil, dengan memberi tahu compiler untuk mempercayai Anda bahwa tidak akan pernah ada nilai nil di sana, sehingga memungkinkan aplikasi Anda untuk dikompilasi.

Ada beberapa skenario yang menyebabkan jenis kesalahan fatal ini:

  1. membuka paksa:

    let user = someVariable!
    

    Jika someVariablenihil, maka Anda akan mengalami crash. Dengan melakukan force unwrap, Anda memindahkan tanggung jawab pemeriksaan nil dari kompilator ke Anda, pada dasarnya dengan melakukan buka paksa Anda menjamin kepada kompilator bahwa Anda tidak akan pernah memiliki nilai nil di sana. Dan coba tebak apa yang terjadi jika entah bagaimana nilai nihil diakhiri someVariable?

    Larutan? Gunakan pengikatan opsional (alias if-let), lakukan pemrosesan variabel di sana:

    if user = someVariable {
        // do your stuff
    }
    
  2. gips paksa (turun):

    let myRectangle = someShape as! Rectangle
    

    Di sini dengan casting paksa Anda memberi tahu kompiler untuk tidak lagi khawatir, karena Anda akan selalu memiliki Rectangleinstance di sana. Dan selama itu berlaku, Anda tidak perlu khawatir. Masalah dimulai saat Anda atau kolega Anda dari proyek mulai mengedarkan nilai non-persegi panjang.

    Larutan? Gunakan pengikatan opsional (alias if-let), lakukan pemrosesan variabel di sana:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
    
  3. Opsional yang terbuka secara implisit. Anggaplah Anda memiliki definisi kelas berikut:

    class User {
        var name: String!
    
        init() {
            name = "(unnamed)"
        }
    
        func nicerName() {
            return "Mr/Ms " + name
        }
    }
    

    Sekarang, jika tidak ada yang mengacaukan nameproperti dengan menyetelnya nil, maka itu berfungsi seperti yang diharapkan, namun jika Userdiinisialisasi dari JSON yang tidak memiliki namekunci, maka Anda mendapatkan kesalahan fatal saat mencoba menggunakan properti.

    Larutan? Jangan gunakan :) Kecuali Anda 102% yakin bahwa properti akan selalu memiliki nilai non-nil pada saat perlu digunakan. Dalam kebanyakan kasus, mengonversi menjadi opsional atau non-opsional akan berfungsi. Menjadikannya non-opsional juga akan menghasilkan kompiler yang membantu Anda dengan memberi tahu jalur kode yang Anda lewatkan memberikan nilai ke properti itu

  4. Outlet yang tidak terhubung atau belum terhubung. Ini adalah kasus tertentu dari skenario # 3. Pada dasarnya Anda memiliki beberapa kelas yang memuat XIB yang ingin Anda gunakan.

    class SignInViewController: UIViewController {
    
        @IBOutlet var emailTextField: UITextField!
    }
    

    Sekarang jika Anda melewatkan menghubungkan outlet dari editor XIB, maka aplikasi akan macet segera setelah Anda ingin menggunakan outlet. Larutan? Pastikan semua outlet terhubung. Atau gunakan ?operator pada mereka: emailTextField?.text = "[email protected]". Atau nyatakan outlet sebagai opsional, meskipun dalam kasus ini kompilator akan memaksa Anda untuk membukanya di seluruh kode.

  5. Nilai yang berasal dari Objective-C, dan tidak memiliki anotasi nullability. Mari kita asumsikan kita memiliki kelas Objective-C berikut:

    @interface MyUser: NSObject
    @property NSString *name;
    @end
    

    Sekarang jika tidak ada anotasi nullability yang ditentukan (baik secara eksplisit atau melalui NS_ASSUME_NONNULL_BEGIN/ NS_ASSUME_NONNULL_END), nameproperti tersebut akan diimpor di Swift sebagai String!(IUO - opsional yang dibuka secara implisit). Segera setelah beberapa kode swift ingin menggunakan nilainya, itu akan macet jika namenihil.

    Larutan? Tambahkan anotasi nullability ke kode Objective-C Anda. Berhati-hatilah, compiler Objective-C agak permisif dalam hal nullability, Anda mungkin akan mendapatkan nilai nil, bahkan jika Anda secara eksplisit menandainya sebagai nonnull.

2 Honey Nov 01 2018 at 11:39

Ini lebih merupakan komentar yang penting dan itulah mengapa opsional yang tidak terbungkus secara implisit dapat menipu ketika menyangkut nilnilai debug .

Pikirkan kode berikut: Ini dikompilasi tanpa kesalahan / peringatan:

c1.address.city = c3.address.city

Namun pada saat runtime memberikan kesalahan berikut: Kesalahan fatal: Tidak terduga ditemukan nihil saat membuka bungkus nilai Opsional

Bisakah Anda memberi tahu saya benda apa itu nil?

Tidak boleh!

Kode lengkapnya adalah:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var c1 = NormalContact()
        let c3 = BadContact()

        c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
    }
}

struct NormalContact {
    var address : Address = Address(city: "defaultCity")
}

struct BadContact {
    var address : Address!
}

struct Address {
    var city : String
}

Singkat cerita dengan menggunakan var address : Address!Anda menyembunyikan kemungkinan bahwa variabel dapat berasal nildari pembaca lain. Dan ketika itu crash Anda seperti "apa-apaan ini ?! saya addressbukan opsional, jadi kenapa saya crash?!.

Karenanya lebih baik menulis seperti itu:

c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 

Bisakah Anda sekarang memberi tahu saya benda apa itu nil?

Kali ini kode telah dibuat lebih jelas untuk Anda. Anda dapat merasionalisasi dan berpikir bahwa kemungkinan besar itu adalah addressparameter yang dibuka secara paksa.

Kode lengkapnya adalah:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var c1 = NormalContact()
        let c2 = GoodContact()

        c1.address.city = c2.address!.city
        c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
        c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
        if let city = c2.address?.city {  // safest approach. But that's not what I'm talking about here. 
            c1.address.city = city
        }

    }
}

struct NormalContact {
    var address : Address = Address(city: "defaultCity")
}

struct GoodContact {
    var address : Address?
}

struct Address {
    var city : String
}
2 AleMohamad May 21 2019 at 00:34

Kesalahan EXC_BAD_INSTRUCTIONdan fatal error: unexpectedly found nil while implicitly unwrapping an Optional valuepaling banyak muncul saat Anda menyatakan @IBOutlet, tetapi tidak terhubung ke papan cerita .

Anda juga harus mempelajari tentang cara kerja Opsional , yang disebutkan dalam jawaban lain, tetapi ini adalah satu-satunya waktu yang paling terlihat oleh saya.

2 ShigaSuresh Feb 13 2020 at 01:16

Jika Anda mendapatkan kesalahan ini di CollectionView, coba buat file CustomCell dan Custom xib juga.

tambahkan kode ini di ViewDidLoad () di mainVC.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
MikeVolmar Sep 26 2019 at 22:11

Saya menemukan kesalahan ini saat membuat segue dari pengontrol tampilan tabel ke pengontrol tampilan karena saya lupa menentukan nama kelas khusus untuk pengontrol tampilan di papan cerita utama.

Sesuatu yang sederhana yang patut diperiksa jika semuanya terlihat baik-baik saja