Swift: jaga biarkan vs jika biarkan

132

Saya telah membaca tentang Opsional di Swift, dan saya telah melihat contoh di mana if letdigunakan untuk memeriksa apakah Opsional memiliki nilai, dan jika memang demikian - lakukan sesuatu dengan nilai yang tidak terbungkus.

Namun, saya telah melihat bahwa di Swift 2.0 kata kunci guard letpaling banyak digunakan. Saya bertanya-tanya apakah if lettelah dihapus dari Swift 2.0 atau apakah masih memungkinkan untuk digunakan.

Haruskah saya mengubah program saya yang berisi if letmenjadi guard let?

lmiguelvargasf
sumber

Jawaban:

165

if letdan guard letmelayani tujuan yang serupa, tetapi berbeda.

Kasus "lain" guardharus keluar dari cakupan saat ini. Umumnya itu berarti harus memanggil returnatau membatalkan program. guarddigunakan untuk memberikan pengembalian awal tanpa memerlukan penumpukan sisa fungsi lainnya.

if letmenyarangkan ruang lingkupnya, dan tidak memerlukan sesuatu yang khusus darinya. Bisa returnatau tidak.

Secara umum, jika if-letblok akan menjadi fungsi lainnya, atau elseklausulnya memiliki returnatau dibatalkan di dalamnya, maka Anda harus menggunakan guardsebagai gantinya. Ini sering berarti (setidaknya menurut pengalaman saya), ketika ragu, guardbiasanya merupakan jawaban yang lebih baik. Tetapi ada banyak situasi di mana if letmasih sesuai.

Rob Napier
sumber
39
Gunakan if letjika non-nilkasusnya valid. Gunakan guardjika nilcase menunjukkan semacam kesalahan.
BallpointBen
4
@BallpointBen Saya tidak setuju dengan itu. Ada banyak kasus di mana guardsesuai bahkan jika tidak ada kesalahan. Terkadang itu berarti tidak ada yang bisa dilakukan. Misalnya, positionTitlemetode mungkin guard if let title = title else {return}. Judul mungkin opsional, dalam hal ini ini bukan kesalahan. Tapi guard letmasih sesuai.
Rob Napier
1
Ya; Maksud saya penjaga membiarkan komentar.
Rob Napier
1
dengan kata lain, "guard let" digunakan saat kode 99% yakin tidak menggunakan syarat lain; Sedangkan "if let" jika kodenya 50 - 50 (contoh) gunakan kondisi else.
Chino Pan
1
Variabel yang diikat if lethanya terlihat di dalam if let ruang lingkup. Variabel terikat oleh guard letterlihat sesudahnya. Jadi masuk akal untuk menggunakan pelindung untuk mengikat nilai opsional juga.
boweidmann
106

Penjaga dapat meningkatkan kejelasan

Ketika Anda menggunakan penjaga, Anda memiliki harapan yang jauh lebih tinggi untuk penjaga untuk berhasil dan agak penting jika tidak berhasil, maka Anda hanya ingin keluar dari ruang lingkup lebih awal . Seperti Anda menjaga untuk melihat apakah file / gambar ada, apakah array isEmpty atau tidak.

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

Jika Anda menulis kode di atas dengan if-let itu menyampaikan kepada pengembang membaca bahwa itu lebih dari 50-50. Tetapi jika Anda menggunakan pelindung, Anda menambahkan kejelasan pada kode Anda dan itu menyiratkan bahwa saya mengharapkan ini bekerja 95% dari waktu ... jika pernah gagal, saya tidak tahu mengapa itu terjadi; itu sangat tidak mungkin ... tapi kemudian gunakan saja gambar default ini sebagai gantinya atau mungkin hanya menegaskan dengan pesan bermakna yang menjelaskan apa yang salah!

  • Hindari guardketika mereka menciptakan efek samping, pelindung digunakan sebagai aliran alami . Hindari penjaga ketika elseklausul memperkenalkan efek samping. Penjaga menetapkan kondisi yang diperlukan agar kode dapat dijalankan dengan benar, menawarkan keluar lebih awal

  • Saat Anda melakukan komputasi signifikan di cabang positif, lakukan refaktorisasi dari ifke guardpernyataan dan kembalikan nilai fallback di elseklausa

Dari: Buku Swift Style Erica Sadun

Juga sebagai hasil dari saran di atas dan kode bersih, kemungkinan besar Anda ingin / perlu menambahkan pernyataan ke dalam pernyataan penjaga yang gagal , ini hanya meningkatkan keterbacaan dan menjelaskan kepada pengembang lain apa yang Anda harapkan.

guard​ ​let​ image =UIImage(named: selectedImageName) else { // YESSSSSS
     assertionFailure("Missing ​​\(​selectedImageName​)​​ asset") 
     return
} 

guard​ ​let​ image =UIImage(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}

Dari: Buku Swift Style Erica Sadun + beberapa modifikasi

(Anda tidak akan menggunakan asserts / preconditions untuk if-lets. Sepertinya tidak benar)

Menggunakan penjaga juga membantu Anda meningkatkan kejelasan dengan menghindari piramida malapetaka. Lihat jawaban Nitin .


Penjaga membuat variabel baru

Ada satu perbedaan penting yang saya yakin belum ada yang bisa menjelaskan dengan baik.

Keduanya guard letdan if let buka variabelnya

Dengan guard letAnda membuat variabel baru yang akan ada di luar elsepernyataan.

Dengan if letAnda tidak membuat variabel baru — setelah pernyataan else, Anda hanya memasukkan blok kode jika opsionalnya bukan nil. Variabel yang baru dibuat hanya ada di dalam blok kode bukan setelahnya!

guard let:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

Untuk info lebih lanjut tentang, if letlihat: Mengapa deklarasi ulang pengikatan opsional tidak membuat kesalahan


Penjaga membutuhkan ruang lingkup keluar

(Juga disebutkan dalam jawaban Rob Napier):

Anda HARUS telah guardmendefinisikan di dalam sebuah func. Tujuan utamanya adalah untuk membatalkan / mengembalikan / keluar dari lingkup, jika kondisi tidak terpenuhi:

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

Karena if letAnda tidak perlu memilikinya di dalam fungsi apa pun:

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}

guard vs. if

Itu perlu dicatat bahwa itu lebih tepat untuk melihat pertanyaan ini sebagai guard letvs if letdan guardvs if.

Standalone iftidak membuka bungkusnya, begitu pula standalone guard. Lihat contoh di bawah ini. Itu tidak keluar lebih awal jika nilainya nil. Tidak ada nilai opsional. Itu hanya keluar lebih awal jika suatu kondisi tidak terpenuhi.

let array = ["a", "b", "c"]
func subscript(at index: Int) -> String?{
   guard index > 0, index < array.count  else { return nil} // exit early with bad index
   return array[index]
}
Madu
sumber
46

Kapan menggunakan if-letdan kapan menggunakan guardsering kali menjadi pertanyaan tentang gaya.

Katakanlah Anda memiliki func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intdan array item opsional ( var optionalArray: [SomeType]?), dan Anda perlu mengembalikan 0jika array adalah nil(not-set) atau countjika array memiliki nilai (disetel).

Anda bisa menerapkannya seperti ini menggunakan if-let:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

atau seperti ini menggunakan guard:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

Contoh-contohnya identik secara fungsional.

Yang guardbenar-benar bersinar adalah ketika Anda memiliki tugas seperti memvalidasi data, dan Anda ingin fungsi gagal lebih awal jika ada yang salah.

Alih-alih menumpuk sekumpulan if-lets saat Anda mendekati penyelesaian validasi, "jalur sukses" dan opsional yang sekarang berhasil terikat semuanya ada dalam cakupan utama metode, karena jalur kegagalan telah kembali semuanya.

divergio
sumber
30

Saya akan mencoba menjelaskan kegunaan pernyataan penjaga dengan beberapa kode (tidak dioptimalkan).

Anda memiliki UI tempat Anda memvalidasi bidang teks untuk pendaftaran pengguna dengan nama depan, nama belakang, email, telepon, dan kata sandi.

Jika ada bidang teks yang tidak berisi teks yang valid, bidang itu harus menjadi firstResponder.

berikut adalah kode yang tidak dioptimalkan:

//pyramid of doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

Anda dapat melihat di atas, bahwa semua string (firstNameString, lastNameString, dll) hanya dapat diakses dalam cakupan pernyataan if. sehingga menciptakan "piramida malapetaka" ini dan memiliki banyak masalah dengannya, termasuk keterbacaan dan kemudahan memindahkan berbagai hal (jika urutan bidang diubah, Anda harus menulis ulang sebagian besar kode ini)

Dengan pernyataan penjaga (dalam kode di bawah), Anda dapat melihat bahwa string ini tersedia di luar {}dan digunakan, jika semua bidang valid.

// guard let no pyramid of doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

Jika urutan bidang berubah, cukup pindahkan baris kode masing-masing ke atas atau ke bawah, dan Anda siap melakukannya.

Ini adalah penjelasan yang sangat sederhana dan kasus penggunaan. Semoga ini membantu!

Nitin Alabur
sumber
14

Perbedaan Dasar

Penjaga biarkan

  1. Proses awal yang ada dari ruang lingkup
  2. Membutuhkan skor yang ada seperti kembali, Lempar dll.
  3. Buat variabel baru yang dapat diakses di luar cakupan.

jika membiarkan

  1. Tidak dapat mengakses ruang lingkup.
  2. tidak perlu mengembalikan pernyataan. Tapi kita bisa menulis

CATATAN: Keduanya digunakan untuk membuka bungkus variabel Opsional.

Kiran K
sumber
2

Penjelasan paling jelas yang saya lihat ada di Github Swift Style Guide :

if menambahkan tingkat kedalaman:

if n.isNumber {
    // Use n here
} else {
    return
}

guard tidak:

guard n.isNumber else {
    return
}
// Use n here
wildpeaks
sumber
2

menjaga

  • Sebuah guardpernyataan digunakan untuk program transfer kontrol dari lingkup jika satu atau lebih kondisi tidak terpenuhi.

  • Nilai kondisi apa pun dalam guardpernyataan harus dari tipe Bool atau tipe yang dihubungkan Bool. Kondisi ini juga dapat berupa deklarasi pengikatan opsional.

Pernyataan penjaga memiliki bentuk berikut:

guard condition else {
    //Generally return
}

jika membiarkan

  • Juga populer sebagai penjilidan opsional .
  • Untuk mengakses objek opsional kami gunakan if let.
if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}
Akshay
sumber
1

Saya belajar ini dari cepat dengan Bob ..

Khas Else-If

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Masalah dengan Else-If

  1. Tanda kurung bersarang
  2. Harus membaca setiap baris untuk menemukan pesan kesalahan

Pernyataan Penjaga Blok penjaga hanya berjalan jika kondisinya salah, dan akan keluar dari fungsi melalui pengembalian. Jika kondisinya benar, Swift mengabaikan blok penjaga. Ini memberikan jalan keluar awal dan tanda kurung yang lebih sedikit. +

func checkDrinkProgram() {
       let iCanDrink = true

           guard iCanDrink else {
        // if iCanDrink == false, run this block
         print("Let's me take you to the jail")
          return
        }

         print("You may drink")
           // You may move on
                  // Come on.
                 // You may leave
                // You don't need to read this.
                 // Only one bracket on the bottom: feeling zen.
       }

Unwrap Opsional dengan Else-If

Pernyataan penjaga tidak hanya berguna untuk mengganti blok bersyarat tipikal dengan pernyataan else-if, tetapi juga bagus untuk membuka bungkusan opsional dengan meminimalkan jumlah tanda kurung. Untuk membandingkan, pertama-tama mari kita mulai cara membuka bungkus beberapa pilihan dengan else-if. Pertama, mari kita buat tiga pilihan yang akan dibuka bungkusnya.

var publicName: String? = "Bob Lee"
var publicPhoto: String? = "Bob's Face"
var publicAge: Int? = nil

Mimpi Buruk Terburuk

func unwrapOneByOne() {
         if let name = publicName {
              if let photo = publicPhoto {
                     if let age = publicAge {
                        print("Bob: \(name), \(photo), \(age)")
                                  } else {
                          print("age is mising")
                           }
                  } else {
                      print("photo is missing")
                         }
                  } else {
                        print("name is missing")
                         }
                  }

Kode di atas pasti berfungsi tetapi melanggar prinsip KERING. Ini mengerikan. Mari kita hancurkan. +

Sedikit Lebih Baik Kode di bawah ini lebih mudah dibaca daripada di atas. +

func unwrapBetter() {
         if let name = publicName {
       print("Yes name")
                   } else {
               print("No name")
        return
      }

         if let photo = publicPhoto {
             print("Yes photo")
            } else {
           print("No photo")
       return
             }

        if let age = publicAge {
            print("Yes age")
                      } else {
                print("No age")
            return
                           }
     }

Unwrap dengan Guard Pernyataan else-if bisa diganti dengan guard. +

 func unwrapOneByOneWithGuard() {
             guard let name = publicName else {
                  print("Name missing")
              return
                                        }

              guard let photo = publicPhoto else {
              print("Photo missing")
                return
                                            }

                  guard let age = publicAge else {
                   print("Age missing")
                                     return
                                                 }
                 print(name)
                 print(photo)
                 print(age)
         }

Unwrap Beberapa Opsional dengan Else-If Sejauh ini, Anda telah membuka bungkus pilihan satu per satu. Swift memungkinkan kita membuka beberapa pilihan sekaligus. Jika salah satunya berisi nil, blok else akan dieksekusi.

func unwrap() {
  if let name = publicName, let photo = publicPhoto, let age = publicAge {
    print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
  } else {
    // if any one of those is missing
    print("Something is missing")
  }
}

Ketahuilah bahwa saat Anda membuka beberapa opsional sekaligus, Anda tidak dapat mengidentifikasi mana yang berisi nil

Unwrap Beberapa Pilihan dengan Guard Tentu saja, kita harus menggunakan pelindung di atas else-if. +

func unwrapWithGuard() {
  guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
    // if one or two of the variables contain "nil"
    print("Something is missing")
    return
  }

  print("Your name is \(name). I see your, \(photo). You are \(age).")
  // Animation Logic
  // Networking
  // More Code, but still zen
}
Ourang-Zeb Khan
sumber
silakan kembali dan perbaiki pemformatan / indentasi kode Anda!
pkamb