Swift: Menguji opsional untuk nol

137

Saya menggunakan Xcode 6 Beta 4. Saya memiliki situasi aneh ini di mana saya tidak tahu bagaimana cara menguji untuk opsional.

Jika saya memiliki xyz opsional, adalah cara yang benar untuk menguji:

if (xyz) // Do something

atau

if (xyz != nil) // Do something

Dokumen mengatakan untuk melakukannya dengan cara pertama, tetapi saya telah menemukan bahwa kadang-kadang, cara kedua diperlukan, dan tidak menghasilkan kesalahan kompiler, tetapi di lain waktu, cara kedua menghasilkan kesalahan kompilator.

Contoh spesifik saya adalah menggunakan parser GData XML yang dijembatani ke swift:

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

Di sini, jika saya baru saja melakukannya:

if xmlError

itu akan selalu kembali benar. Namun, jika saya lakukan:

if (xmlError != nil)

lalu bekerja (seperti cara kerjanya di Objective-C).

Apakah ada sesuatu dengan XML GData dan cara memperlakukan opsional yang saya lewatkan?

tng
sumber
4
Bisakah kita melihat contoh lengkap untuk kasus tak terduga dan kesalahan Anda? (Dan saya tahu ini sulit, tetapi cobalah untuk mulai kehilangan tanda kurung di sekitar kondisional!)
Matt Gibson
1
ini diubah dalam Xcode 6 beta 5
newacct
Saya baru saja memperbarui pertanyaan dengan kasus yang tidak terduga.
tng
Saya belum memperbarui ke beta 5. Saya akan segera melakukannya; senang melihat ?? di Swift, dan perilaku opsional yang lebih konsisten.
tng

Jawaban:

172

Di Xcode Beta 5, mereka tidak lagi membiarkan Anda melakukannya:

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

Ini menghasilkan kesalahan:

tidak sesuai dengan protokol 'BooleanType.Protocol'

Anda harus menggunakan salah satu dari formulir ini:

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}
ktzhang
sumber
5
Adakah yang bisa menjelaskan mengapa xyz == nil tidak berfungsi?
Christoph
Contoh kedua harus berbunyi: // Lakukan sesuatu menggunakan xy (yang merupakan perbedaan utama dalam kasus penggunaan antara dua varian)
Alper
@Christoph: Pesan kesalahannya adalah konstanta 'xyz' yang digunakan sebelum diinisialisasi. Saya pikir Anda perlu menambahkan '= nihil' di akhir baris deklarasi xyz.
user3207158
Bagi mereka yang baru cepat seperti saya dan bertanya-tanya: Anda masih harus membuka xyz di dalam blok if. Ini aman, bahkan jika Anda memaksa membuka
Omar
@Omar Itulah keindahan bentuk kedua, Anda menggunakan xy di dalam blok tanpa harus membukanya.
Erik Doernenburg
24

Untuk menambah jawaban lain, alih-alih menugaskan variabel yang berbeda nama di dalam suatu ifkondisi:

var a: Int? = 5

if let b = a {
   // do something
}

Anda dapat menggunakan kembali nama variabel yang sama seperti ini:

var a: Int? = 5

if let a = a {
    // do something
}

Ini mungkin membantu Anda menghindari kehabisan nama variabel kreatif ...

Ini mengambil keuntungan dari variabel shadowing yang didukung di Swift.

zevij
sumber
18

Salah satu cara paling langsung untuk menggunakan opsional adalah sebagai berikut:

Dengan asumsi xyzadalah tipe opsional, seperti Int?misalnya.

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

Dengan cara ini Anda bisa menguji apakah xyzmengandung nilai dan jika demikian, segera bekerja dengan nilai itu.

Sehubungan dengan kesalahan kompiler Anda, tipe UInt8ini tidak opsional (perhatikan no '?') Dan karena itu tidak dapat dikonversi ke nil. Pastikan variabel yang Anda gunakan adalah opsional sebelum Anda memperlakukannya seperti variabel.

Isaac Drachman
sumber
1
Saya menemukan metode ini menjadi buruk karena saya membuat variabel baru (konstan) tidak perlu, dan harus membuat nama baru yang sebagian besar waktu akan menjadi yang terburuk dari sebelumnya. Membawa saya lebih banyak waktu untuk menulis dan untuk pembaca.
Lombas
3
Ini adalah metode standar dan metode yang disarankan untuk membuka bungkusan variabel opsional. "'jika dibiarkan" sangat kuat.
gnasher729
@ gnasher729 tetapi bagaimana jika Anda hanya ingin menggunakan bagian yang lain
Brad Thomas
15

Swift 3.0, 4.0

Terutama ada dua cara memeriksa opsional untuk nol. Berikut adalah contoh dengan perbandingan di antara mereka

1. jika dibiarkan

if letadalah cara paling dasar untuk memeriksa opsi nihil. Kondisi lain dapat ditambahkan pada pemeriksaan nil ini, dipisahkan dengan koma. Variabel tidak boleh nol untuk pindah ke kondisi berikutnya. Jika hanya diperlukan pemeriksaan nil, hapus ketentuan tambahan dalam kode berikut.

Selain itu, jika xtidak nol, penutupan jika akan dieksekusi dan x_valakan tersedia di dalam. Kalau tidak, penutupan lain dipicu.

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2. penjaga biarkan

guard letdapat melakukan hal serupa. Tujuan utamanya adalah untuk membuatnya lebih masuk akal secara logis. Ini seperti mengatakan Pastikan variabel tidak nol, jika tidak hentikan fungsinya . guard letjuga dapat melakukan pengecekan kondisi tambahan sebagaiif let .

Perbedaannya adalah bahwa nilai yang terbuka akan tersedia pada cakupan yang sama seperti guard let, seperti yang ditunjukkan dalam komentar di bawah ini. Hal ini juga mengarah ke titik yang di penutupan lain, program ini memiliki untuk keluar dari lingkup saat ini, oleh return, break, dll

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope
Fangming
sumber
11

Dari panduan pemrograman cepat

Jika Pernyataan dan Paksa Dibongkar

Anda bisa menggunakan pernyataan if untuk mengetahui apakah opsional mengandung nilai. Jika opsional memang memiliki nilai, itu bernilai true; jika tidak memiliki nilai sama sekali, itu bernilai false.

Jadi cara terbaik untuk melakukan ini adalah

// swift > 3
if xyz != nil {}

dan jika Anda menggunakan xyzpernyataan if. Kemudian Anda dapat membuka xyzjika pernyataan dalam variabel konstan. Jadi Anda tidak perlu membuka setiap tempat di jika pernyataan di mana xyzdigunakan.

if let yourConstant = xyz{
      //use youtConstant you do not need to unwrap `xyz`
}

Konvensi ini disarankan oleh appledan akan diikuti oleh para penyembah.

codester
sumber
2
Di Swift 3 harus Anda lakukan if xyz != nil {...}.
psmythirl
Dalam Swift 3, opsional tidak dapat digunakan sebagai boolean. Pertama karena itu adalah C-isme yang seharusnya tidak ada dalam bahasa di tempat pertama, dan kedua karena itu menyebabkan kebingungan mutlak dengan Bool opsional.
gnasher729
9

Walaupun Anda masih harus secara eksplisit membandingkan opsi dengan nilatau menggunakan penjilidan opsional untuk mengekstraksi nilainya secara tambahan (yaitu, opsional tidak secara implisit dikonversi menjadi nilai Boolean), perlu dicatat bahwa Swift 2 telah menambahkan guardpernyataan untuk membantu menghindari piramida kiamat ketika bekerja dengan beberapa nilai opsional.

Dengan kata lain, opsi Anda sekarang termasuk memeriksa secara eksplisit untuk nil:

if xyz != nil {
    // Do something with xyz
}

Ikatan opsional:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

Dan guardpernyataan:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

Perhatikan bagaimana penjilidan opsional biasa dapat mengarah ke indentasi yang lebih besar ketika ada lebih dari satu nilai opsional:

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

Anda dapat menghindari bersarang ini dengan guardpernyataan:

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz
Chris Frederick
sumber
1
seperti yang disebutkan di atas, Anda dapat melakukan ini juga untuk menyingkirkan statmenet opsional bersarang. jika biarkan abc = abc? .xyz? .something {}
Suhaib
1
@Suhaib Ah, true: Anda juga dapat menggunakan perangkaian opsional ( developer.apple.com/library/prerelease/content/documentation/… ) untuk mengakses properti bersarang dengan cepat. Saya tidak menyebutkannya di sini karena saya pikir itu mungkin terlalu menyinggung dari pertanyaan awal.
Chris Frederick
Jawaban bagus! Tapi Swift, OMG ... masih jauh dari ramah programmer!
turingtested
@turingtested Terima kasih! Sebenarnya, saya pikir ini adalah programmer ramah dalam arti bahwa hal itu menjamin Anda tidak akan sengaja mencoba untuk menggunakan nilnilai, tapi saya setuju bahwa kurva belajar sedikit curam. :)
Chris Frederick
4

Perpanjangan Protokol 5 Swift

Berikut ini adalah pendekatan menggunakan ekstensi protokol sehingga Anda dapat dengan mudah menyejajarkan nil check opsional:

import Foundation

public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

Pemakaian

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}
Brody Robertson
sumber
Saya mendapat beberapa downvotes pada jawaban ini dan ingin tahu mengapa. Setidaknya komentar jika Anda akan downvote.
Brody Robertson
1
Mungkin swiftanalog dari pythonistasyang mencoba untuk menjaga bahasa dalam bentuk kemurnian pythonic. Saya ambil? Saya baru saja mengangkat kode Anda ke mixin Utils saya.
javadba
2

Sekarang Anda dapat melakukan dengan cepat hal-hal berikut yang memungkinkan Anda untuk mendapatkan kembali sedikit tujuan-c if nil else

if textfieldDate.text?.isEmpty ?? true {

}
Nicolas Manzini
sumber
2

Alih-alih if, operator ternary mungkin berguna ketika Anda ingin mendapatkan nilai berdasarkan apakah ada sesuatu yang nihil:

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}
qed
sumber
xyz == nilEkspresi tidak bekerja, tetapi x != nilbekerja. Tidak tahu kenapa
Andrew
2

Pendekatan lain selain menggunakan ifatau guardpernyataan untuk melakukan penjilidan opsional adalah untuk memperluas Optionaldengan:

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValuemenerima penutupan dan menyebutnya dengan nilai sebagai argumen ketika opsional tidak nol. Digunakan seperti ini:

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

Anda mungkin harus menggunakan ifatau guardkarena itu adalah pendekatan yang paling konvensional (sehingga akrab) yang digunakan oleh programmer Swift.

Tiago
sumber
2

Salah satu opsi yang belum dicakup secara khusus adalah menggunakan sintaks nilai yang diabaikan Swift:

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

Saya suka ini karena memeriksa nilrasanya tidak pada tempatnya dalam bahasa modern seperti Swift. Saya pikir alasannya terasa tidak pada tempatnya adalah bahwa nilpada dasarnya nilai sentinel. Kami telah menghilangkan banyak penjaga di tempat lain dalam pemrograman modern sehingga nilterasa seperti itu juga harus dilakukan.

Ben Lachman
sumber
1

Anda juga bisa menggunakannya Nil-Coalescing Operator

The nil-coalescing operator( a ?? b) membuka bungkusan opsional ajika mengandung anilai, atau mengembalikan anilai default bjika aada nil. Ekspresi a selalu dari tipe opsional. Ekspresi bharus cocok dengan tipe yang disimpan di dalamnya a.

let value = optionalValue ?? defaultValue

Jika optionalValueadalah nil, itu secara otomatis memberikan nilai kepadadefaultValue

yoAlex5
sumber
Saya suka itu, karena memberikan opsi mudah untuk menentukan nilai default.
thowa
0
var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

Tes ini berfungsi seperti yang diharapkan dalam semua kasus. BTW, Anda tidak perlu tanda kurung ().

Mundi
sumber
2
Kasus OP yang bersangkutan tentang adalah: if (xyz != nil).
zaph
0

Jika Anda memiliki persyaratan dan ingin membuka dan membandingkan, bagaimana dengan memanfaatkan evaluasi hubung singkat ekspresi senyawa boolean seperti pada

if xyz != nil && xyz! == "some non-nil value" {

}

Memang, ini tidak dapat dibaca seperti beberapa posting yang disarankan lainnya, tetapi menyelesaikan pekerjaan dan agak ringkas daripada solusi yang disarankan lainnya.

RT Denver
sumber