Kata kunci penjaga Swift

197

Swift 2 memperkenalkan guardkata kunci, yang dapat digunakan untuk memastikan bahwa berbagai data dikonfigurasikan siap untuk digunakan. Contoh yang saya lihat di situs web ini menunjukkan fungsi submitTapped:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Saya bertanya-tanya apakah menggunakan guarditu berbeda dari melakukannya dengan cara lama, menggunakan suatu ifkondisi. Apakah itu memberi manfaat, yang tidak bisa Anda dapatkan dengan menggunakan cek sederhana?

David Snabel
sumber
Lihat juga pertanyaan penjaga vs jika-biarkan
Honey
Silakan lihat tautan berikut ini media.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Jawaban:

368

Membaca artikel ini saya perhatikan manfaat besar menggunakan Guard

Di sini Anda dapat membandingkan penggunaan penjaga dengan contoh:

Ini adalah bagian tanpa penjaga:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Di sini Anda meletakkan kode yang Anda inginkan dalam semua kondisi

    Anda mungkin tidak langsung melihat masalah dengan ini, tetapi Anda bisa membayangkan betapa membingungkannya jika itu bersarang dengan berbagai kondisi yang semua harus dipenuhi sebelum menjalankan pernyataan Anda

Cara untuk membersihkan ini adalah dengan melakukan masing-masing cek Anda terlebih dahulu, dan keluar jika ada yang tidak dipenuhi. Ini memudahkan pemahaman tentang kondisi apa yang akan membuat fungsi ini keluar.

Tapi sekarang kita bisa menggunakan penjaga dan kita bisa melihat itu mungkin untuk menyelesaikan beberapa masalah:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Memeriksa kondisi yang Anda inginkan, bukan kondisi yang tidak Anda inginkan. Ini lagi mirip dengan pernyataan. Jika kondisi tidak terpenuhi, pernyataan penjaga lain dijalankan, yang keluar dari fungsi.
  2. Jika kondisi berlalu, variabel opsional di sini secara otomatis terbuka untuk Anda dalam lingkup yang disebut pernyataan penjaga - dalam hal ini, fungsi fooGuard (_ :).
  3. Anda memeriksa kasus-kasus buruk lebih awal, membuat fungsi Anda lebih mudah dibaca dan lebih mudah dirawat

Pola yang sama juga berlaku untuk nilai-nilai non-opsional:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

Jika Anda masih memiliki pertanyaan, Anda dapat membaca seluruh artikel: Pernyataan penjaga cepat.

Membungkus

Dan akhirnya, membaca dan menguji saya menemukan bahwa jika Anda menggunakan penjaga untuk membuka opsi,

nilai-nilai yang terbuka itu tetap ada untuk Anda gunakan di sisa blok kode Anda

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Di sini nilai yang terbuka akan tersedia hanya di dalam blok if

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Jorge Casariego
sumber
3
Hai @Eric, Anda melakukan pos yang luar biasa! Terima kasih karena telah membuatnya begitu mudah dimengerti!
Jorge Casariego
1
Saya menggunakan penjaga untuk untuk membuka NSError. Tetapi ketika saya mencoba menggunakannya dalam lingkup penjaga (untuk mengirimkan kesalahan ke beberapa panggilan balik misalnya) ia mengatakan "Variabel yang dinyatakan dalam kondisi penjaga tidak dapat digunakan dalam tubuhnya". Apakah ini masuk akal? Terima kasih
GeRyCh
6
@ GeRyCh membuka bungkus dalam pernyataan penjaga membuat variabel itu tersedia untuk setelah pernyataan penjaga, bukan di dalamnya. Butuh waktu beberapa saat untuk terbiasa dengan ini.
DonnaLea
2
Inilah artikel bagus lainnya tentang cara menggunakan pelindung untuk membuka bungkusan opsional. Ringkas dengan baik.
Doches
let x = x where x > 0apakah berarti Anda telah digabungkan kondisi lain ke dalam opsional mengikat? Maksud saya sedikit berbeda dariif let constantName = someOptional { statements }
Sayang
36

Tidak seperti if, guardmenciptakan variabel yang dapat diakses dari luar bloknya. Ini berguna untuk membuka banyak Optionals.

takebayashi
sumber
24

Sebenarnya ada dua manfaat besar untuk guard. Salah satunya adalah menghindari piramida malapetaka, seperti yang telah disebutkan orang lain - banyak if letpernyataan menjengkelkan bersarang di dalam satu sama lain bergerak semakin jauh ke kanan.

Manfaat lainnya adalah logika yang ingin Anda terapkan lebih " if not let" daripada " if let { } else".

Berikut ini sebuah contoh: misalkan Anda ingin menerapkan accumulate- persilangan di antara mapdan di reducemana ia mengembalikan array yang berjalan berkurang. Ini dia dengan guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

Bagaimana Anda menulisnya tanpa penjaga, tetapi masih menggunakan firstyang mengembalikan opsional? Sesuatu seperti ini:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

Bersarang ekstra mengganggu, tetapi juga, tidak logis untuk memiliki ifdan elsebegitu jauh. Jauh lebih mudah dibaca untuk memiliki pintu keluar awal untuk kasing kosong, dan kemudian melanjutkan dengan fungsi lainnya seolah-olah itu bukan suatu kemungkinan.

Kecepatan Kecepatan Udara
sumber
19

Ketika suatu kondisi terpenuhi menggunakannya guardmengekspos variabel yang dideklarasikan di dalam guardblok ke sisa kode-blok, membawa mereka ke dalam cakupannya. Yang, seperti yang dinyatakan sebelumnya, tentu akan berguna dengan if letpernyataan bersarang .

Perhatikan bahwa penjaga memerlukan pengembalian atau lemparan dalam pernyataannya yang lain.

Parsing JSON dengan Guard

Di bawah ini adalah contoh bagaimana seseorang dapat mengurai objek JSON menggunakan penjaga daripada jika-biarkan. Ini adalah kutipan dari entri blog yang menyertakan file taman bermain yang dapat Anda temukan di sini:

Cara menggunakan Guard di Swift 2 untuk mengurai JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}

unduh taman bermain: taman bermain guard

Info lebih lanjut:

Berikut adalah kutipan dari Panduan Bahasa Pemrograman Bahasa Swift:

Jika kondisi pernyataan penjaga terpenuhi, eksekusi kode berlanjut setelah penjepit penutupan pernyataan penjaga. Setiap variabel atau konstanta yang diberi nilai menggunakan pengikatan opsional sebagai bagian dari kondisi tersedia untuk sisa blok kode tempat pernyataan penjaga muncul.

Jika kondisi itu tidak terpenuhi, kode di dalam cabang lain dijalankan. Cabang itu harus mentransfer kontrol untuk keluar dari blok kode tempat pernyataan penjaga itu muncul. Ini dapat melakukan ini dengan pernyataan transfer kontrol seperti kembali, istirahat, atau melanjutkan, atau dapat memanggil fungsi atau metode yang tidak kembali, seperti sebagai fatalError ().

Dan Beaulieu
sumber
7

Salah satu manfaatnya adalah mengeliminasi banyak if letpernyataan yang bersarang . Lihat video WWDC "What's New in Swift" sekitar pukul 15:30, bagian berjudul "Pyramid of Doom".

zaph
sumber
6

Kapan harus menggunakan penjaga

Jika Anda memiliki pengontrol tampilan dengan beberapa elemen UITextField atau jenis input pengguna lain, Anda akan segera melihat bahwa Anda harus membuka bungkusan opsional textField.text untuk mendapatkan teks di dalamnya (jika ada!). isEmpty tidak akan membantu Anda di sini, tanpa masukan apa pun, bidang teks hanya akan mengembalikan nol.

Jadi, Anda memiliki beberapa di antaranya yang Anda buka dan akhirnya beralih ke fungsi yang mempostingnya ke titik akhir server. Kami tidak ingin kode server harus berurusan dengan nilai nil atau salah mengirim nilai yang tidak valid ke server sehingga kami akan membuka bungkusan nilai input tersebut dengan penjaga terlebih dahulu.

func submit() {
    guard let name = nameField.text else {
        show("No name to submit")
        return
    }

    guard let address = addressField.text else {
        show("No address to submit")
        return
    }

    guard let phone = phoneField.text else {
        show("No phone to submit")
        return
    }

    sendToServer(name, address: address, phone: phone)
}

func sendToServer(name: String, address: String, phone: String) {
  ...
}

Anda akan melihat bahwa fungsi komunikasi server kami mengambil nilai-nilai String non-opsional sebagai parameter, oleh karena itu penjaga membuka bungkus sebelumnya. Bongkar membuka sedikit tidak intuitif karena kita terbiasa membuka bungkusan dengan jika membiarkan yang membuka nilai untuk digunakan di dalam blok. Di sini pernyataan penjaga memiliki blok terkait tetapi sebenarnya adalah blok lain - yaitu hal yang Anda lakukan jika pembatalan gagal - nilainya terbuka langsung ke konteks yang sama dengan pernyataan itu sendiri.

// pemisahan masalah

Tanpa penjaga

Tanpa menggunakan penjaga, kita akan berakhir dengan tumpukan kode besar yang menyerupai piramida malapetaka . Ini tidak skala dengan baik untuk menambahkan bidang baru ke formulir kami atau membuat kode yang sangat mudah dibaca. Lekukan bisa sulit untuk diikuti, terutama dengan begitu banyak pernyataan di setiap cabang.

func nonguardSubmit() {
    if let name = nameField.text {
        if let address = addressField.text {
            if let phone = phoneField.text {
                sendToServer(name, address: address, phone: phone)
            } else {
                show("no phone to submit")
            }
        } else {
            show("no address to submit")
        }
    } else {
        show("no name to submit")
    }
}

Ya, kami bahkan bisa menggabungkan semua ini jika membiarkan pernyataan menjadi satu pernyataan yang dipisahkan dengan koma tetapi kami akan kehilangan kemampuan untuk mengetahui pernyataan yang gagal dan menyajikan pesan kepada pengguna.

https://thatthinginswift.com/guard-statement-swift/

Madu
sumber
5

Dengan menggunakan penjaga, kehebatan kami jelas. kami tidak ingin menjalankan sisa kode jika kondisi tertentu tidak terpenuhi. di sini kita dapat memperluas rantai juga, silakan lihat kode di bawah ini:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Narendra G
sumber
5

Pernyataan penjaga akan dilakukan. itu berbeda

1) itu memungkinkan saya untuk mengurangi bersarang jika pernyataan
2) itu meningkatkan ruang lingkup saya yang dapat diakses oleh variabel saya

jika Pernyataan

func doTatal(num1 : Int?, num2: Int?) {
  // nested if statement
    if let fistNum = num1 where num1 > 0 {
        if let lastNum = num2 where num2 < 50 {

          let total = fistNum + lastNum
        }
    }
 // don't allow me to access out of the scope 
 //total = fistNum + lastNum 
}

Pernyataan penjaga

func doTatal(num1 : Int?, num2: Int?) {
   //reduce  nested if statement and check positive way not negative way 
    guard let fistNum = num1 where num1 > 0 else{
      return
    }
    guard  let lastNum = num2 where num2 < 50 else {
     return
    }
    // increase my scope which my variable accessible
    let total = fistNum + lastNum

}
Nazmul Hasan
sumber
3

Dari dokumentasi Apple:

Pernyataan Penjaga

Pernyataan penjaga digunakan untuk mentransfer kontrol program di luar ruang lingkup jika satu atau lebih kondisi tidak terpenuhi.

Synatx:

guard condition else {
    statements
}

Keuntungan:

1. Dengan menggunakan guardpernyataan, kita dapat menyingkirkan persyaratan bersarang mendalam yang tujuan satu-satunya adalah memvalidasi seperangkat persyaratan.

2. Ini dirancang khusus untuk keluar dari metode atau fungsi lebih awal.

jika Anda gunakan jika biarkan di bawah ini adalah kode tampilannya.

  let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        if error == nil {
            if let  statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
                if let data = data {

                    //Process Data Here.
                    print("Data: \(data)")

                } else {
                    print("No data was returned by the request!")
                }
            } else {
                print("Your request returned a status code other than 2XX!")
            }
        } else {
            print("Error Info: \(error.debugDescription)")
        }
    }
    task.resume()

Menggunakan pelindung, Anda dapat mentransfer kontrol di luar ruang lingkup jika satu atau beberapa kondisi tidak terpenuhi.

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

            /* GUARD: was there an error? */
            guard (error == nil) else {
                print("There was an error with your request: \(error)")
                return
            }

            /* GUARD: Did we get a successful 2XX response? */
            guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
                print("Your request returned a status code other than 2XX!")
                return
            }

            /* GUARD: was there any data returned? */
            guard let data = data else {
                print("No data was returned by the request!")
                return
            }

            //Process Data Here.
            print("Data: \(data)")
}
task.resume()

Referensi:

1. Swift 2: Keluar Dini Dengan penjaga 2. Udacity 3. Pernyataan Penjaga

Ashok R
sumber
Tapi Anda bisa melakukan yang terakhir dengan pernyataan if? if condition { return }bermuka masam?
Oliver Dixon
2

Seperti pernyataan if, guard mengeksekusi pernyataan berdasarkan nilai ekspresi Boolean. Tidak seperti pernyataan if, pernyataan penjaga hanya berjalan jika kondisinya tidak terpenuhi. Anda bisa menganggap penjagaan lebih seperti Penegasan, tetapi alih-alih menabrak, Anda dapat keluar dengan anggun.

lihat: http://ericcerney.com/swift-guard-statement/

Zgpeace
sumber
1

Itu benar-benar membuat aliran urutan dengan beberapa pencarian dan opsional jauh lebih ringkas dan jelas dan mengurangi banyak jika bersarang. Lihat posting Erica Sadun tentang mengganti Ifs . .... Bisa terbawa, contoh di bawah ini:

    let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
    guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
    guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
    guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}

Lihat apakah itu menempel.

DavidS
sumber
1

Sederhananya, ini menyediakan cara untuk memvalidasi bidang sebelum eksekusi. Ini adalah gaya pemrograman yang baik karena meningkatkan keterbacaan. Dalam bahasa lain, tampilannya mungkin seperti ini:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

Tetapi karena Swift memberi Anda opsional, kami tidak dapat memeriksa apakah itu nihil dan menetapkan nilainya ke variabel. Sebaliknya, if letperiksa bukan nol dan tetapkan variabel untuk menyimpan nilai aktual. Di sinilah guardberperan. Ini memberi Anda cara yang lebih singkat untuk keluar dari awal menggunakan opsional.

Gunby
sumber
1

Sumber: Penjaga di Swift

Mari kita lihat contoh untuk memahaminya dengan jelas

Contoh 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

Dalam contoh di atas kita melihat bahwa 3 lebih besar dari 2 dan pernyataan di dalam klausa guard lainnya dilewati dan True dicetak.

Contoh 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

Dalam contoh di atas kita melihat bahwa 1 lebih kecil dari 2 dan pernyataan di dalam klausa guard lainnya dieksekusi dan False dicetak diikuti oleh return.

Example 3: gaurd let, unwrapping optionals through guard let

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          // Condition is false            return         
     }         
     print("Condition is met\(name)")     
} 
getName(args: "")

Dalam contoh di atas kita menggunakan pelindung biarkan untuk membuka bungkus opsional. Dalam fungsi getName, kami telah mendefinisikan variabel tipe string myName yang opsional. Kami kemudian menggunakan penjaga biarkan untuk memeriksa apakah variabel myName adalah nil atau tidak, jika tidak menetapkan untuk nama dan memeriksa lagi, nama tidak kosong. Jika kedua kondisi tersebut memenuhi syarat yaitu benar, blok yang lain akan dilewati dan mencetak “Ketentuan dipenuhi nama”.

Pada dasarnya kami memeriksa dua hal di sini yang dipisahkan oleh koma, pertama membuka dan opsional dan memeriksa apakah itu memenuhi syarat atau tidak.

Di sini kita melewatkan apa-apa ke fungsi yaitu string kosong dan karenanya Kondisi salah cetak.

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          
     return         
     }        
     print("Condition is met \(name)") // Condition is met Hello    
} getName(args: "Hello")

Di sini kita melewati "Halo" ke fungsi dan Anda dapat melihat output dicetak "Kondisi terpenuhi Halo".

Aditya
sumber