Swift: guard let vs if let


133

Swift'de Opsiyonlar hakkında bir şeyler okudum ve if letbir Opsiyonel değerin bir değer içerip içermediğini kontrol etmek için kullanılan örnekler gördüm ve eğer varsa - açılmamış değerle bir şeyler yapın.

Ancak Swift 2.0'da anahtar kelimenin en guard letçok kullanıldığını gördüm . if letSwift 2.0'dan kaldırıldı mı yoksa hala kullanılabilir mi acaba ?

İçeren programlarımı değiştirmeli miyim? if let için guard let?

Yanıtlar:


165

if letve guard letbenzer ama farklı amaçlara hizmet eder.

"Else" durumu guard, geçerli kapsamdan çıkmalıdır. Genel olarak bu return, programı çağırması veya iptal etmesi gerektiği anlamına gelir . guardişlevin geri kalanının iç içe geçmesini gerektirmeden erken dönüş sağlamak için kullanılır.

if letkapsamını iç içe geçirir ve özel bir şey gerektirmez. Yapabilir returnya da yapamaz.

Genel olarak, eğer if-letblok fonksiyonun geri kalanı olacaksa veya elsecümlesinde bir returnveya iptal olacaksa, guardbunun yerine kullanmalısınız. Bu, genellikle (en azından benim deneyimime göre), şüphe duyduğunuzda, guardgenellikle daha iyi cevap olduğu anlamına gelir . Ancak if letyine de uygun olan birçok durum var .


39
Durum geçerli if letolduğunda kullanın non-nil. Durum bir tür hatayı temsil guardettiğinde kullanın nil.
BallpointBen

4
@BallpointBen Buna katılmıyorum. guardHata olmasa bile uygun olduğu birçok durum vardır. Bazen sadece yapacak bir şey olmadığı anlamına gelir. Örneğin, bir positionTitleyöntem olabilir guard if let title = title else {return}. Başlık isteğe bağlı olabilir, bu durumda bu bir hata değildir. Ama guard letyine de uygun.
Rob Napier

1
Evet; Muhafızın yorum yapmasını kastettim.
Rob Napier

1
başka bir deyişle, kod else koşulunu kullanmadığından% 99 emin olduğunda "koruma izni" kullanılır; Öte yandan, kod 50 - 50 (örnek) olduğunda "if let" else koşulunu kullanmak.
Chino Pan

1
Bağlanan değişken if letyalnızca kapsam içinde görülebilir if let. Bağlanan değişken guard letdaha sonra görülebilir. Bu nedenle, isteğe bağlı değerleri bağlamak için koruma kullanmak da mantıklıdır.
boweidmann

106

Guard netliği artırabilir

Muhafız kullandığınızda , korumanın başarılı olması için çok daha yüksek bir beklentiniz vardır ve eğer başarılı olmazsa, o zaman sadece kapsamdan erken çıkmak istemeniz biraz önemlidir . Bir dosya / görüntünün var olup olmadığını, bir dizi Boş olup olmadığını görmek için koruduğunuz gibi.

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
}

Yukarıdaki kodu if-let ile yazarsanız, okuma geliştiricisine 50-50'den daha fazlası olduğunu iletir. Ama eğer koruma kullanırsanız, kodunuza netlik katarsınız ve bu, bunun% 95 oranında çalışmasını bekliyorum anlamına gelir ... Eğer başarısız olursa, neden olacağını bilmiyorum; pek olası değil ... ama sonra bunun yerine sadece bu varsayılan resmi kullanın ya da belki sadece neyin yanlış gittiğini açıklayan anlamlı bir mesajla iddia edin!

  • guardYan etkiler yarattıklarında kaçının , korumalar doğal akış olarak kullanılmalıdır . elseMaddeler yan etkilere neden olduğunda korumalardan kaçının . Muhafızlar , kodun düzgün çalışması için gerekli koşulları belirler ve erken çıkış sunar

  • Pozitif dalda önemli hesaplamalar gerçekleştirdiğinizde, ' ifdan bir guardifadeye yeniden düzenleme yapın ve elseyan tümcede geri dönüş değerini döndürür

Gönderen: Erica Sadun'un Swift Style kitabı

Ayrıca yukarıdaki önerilerin ve temiz kodun bir sonucu olarak, başarısız koruma ifadelerine iddialar eklemek isteyip istemediğinizi / buna ihtiyaç duymanız daha olasıdır , bu sadece okunabilirliği artırır ve diğer geliştiricilere ne beklediğinizi netleştirir.

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

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

Gönderen: Erica Sadun'un Swift Style kitabı + bazı değişiklikler

( if-letURL'ler için iddiaları / ön koşulları kullanmayacaksınız . Bu doğru görünmüyor)

Muhafız kullanmak ayrıca kıyamet piramidinden kaçınarak netliği artırmanıza yardımcı olur . Nitin'in cevabına bakın .


Guard yeni bir değişken oluşturur

Kimsenin iyi açıklamadığına inandığım önemli bir fark var.

Hem guard letve if let unwrap değişken ancak

İle guard letsen oluştururken olacak yeni bir değişken var dışarıda elseaçıklamada.

İle if letsen yaratmak değildir herhangi bir yeni değişken sonra başka açıklamada, yalnızca girmek kod bloğu ise opsiyonel olmayan sıfırdır. Yeni oluşturulan değişken yalnızca kod bloğunun içinde var olur !

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)
    }
}

Daha fazla bilgi için if letbkz: İsteğe bağlı bağlamanın yeniden bildirilmesi neden bir hata oluşturmaz


Koruma, kapsamdan çıkılmasını gerektirir

(Rob Napier'in cevabında da bahsedilmiştir):

Bir işlev içindeguard tanımlamış olmanız GEREKİR . Bu önemli amacı, / iade / çıkış kapsamını iptal etmektir var ise bir koşul yerine getirilmediği:

var str : String?

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

İçin if letherhangi bir fonksiyon içine olması gerekmez:

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

guard vs if

Bu soruyu guard letvs if letve guardvs olarak görmenin daha uygun olduğunu belirtmek gerekir if.

Tek başına ifbir paket açma işlemi yapmaz, tek başına da yapmaz guard. Aşağıdaki örneğe bakın. Bir değer ise erken çıkmaz nil. İsteğe bağlı değer YOKTUR. Bir koşul karşılanmazsa erken çıkar.

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]
}

46

Ne zaman kullanılacağı if-letve ne zaman kullanılacağı guardgenellikle bir stil meselesidir.

Diyelim ki func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intve isteğe bağlı bir öğe dizisi ( var optionalArray: [SomeType]?) var ve 0dizi nil(ayarlanmamışsa) veya countdizi bir değere sahipse (ayarlanmışsa) geri dönmeniz gerekiyor .

Bunu kullanarak şu şekilde uygulayabilirsiniz if-let:

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

veya şunu kullanarak bunu beğen guard:

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

Örnekler işlevsel olarak aynıdır.

Nerede guard şaşırmaya veri doğrulama gibi bir görevi varken, ve Bir yanlışlık olması durumunda fonksiyon erken başarısız olmasını istiyorum.

if-letDoğrulamayı bitirmeye yaklaştıkça bir grup s'yi iç içe yerleştirmek yerine , "başarı yolu" ve şimdi başarıyla bağlanan seçeneklerin tümü yöntemin ana kapsamındadır, çünkü başarısızlık yollarının tümü zaten geri dönmüştür.


30

Bazı (optimize edilmemiş) bir kodla koruma ifadelerinin yararlılığını açıklamaya çalışacağım.

Ad, soyad, e-posta, telefon ve şifre ile kullanıcı kaydı için metin alanlarını doğruladığınız bir kullanıcı arayüzünüz var.

Herhangi bir textField geçerli metin içermiyorsa, bu alanı firstResponder yapmalıdır.

İşte optimize edilmemiş kod:

//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()
    }
}

Yukarıda, tüm dizelere (firstNameString, lastNameString vb.) Yalnızca if ifadesi kapsamında erişilebildiğini görebilirsiniz. bu yüzden bu "kıyamet piramidi" ni yaratır ve onunla ilgili okunabilirlik ve şeyleri hareket ettirme kolaylığı dahil olmak üzere birçok sorunu vardır (alanların düzeni değişirse, bu kodun çoğunu yeniden yazmanız gerekir)

Guard ifadesiyle (aşağıdaki kodda), bu dizelerin {}tüm alanlar geçerliyse, dışında kullanılabilir olduğunu ve kullanıldığını görebilirsiniz .

// 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)
}

Alanların sırası değişirse, ilgili kod satırlarını yukarı veya aşağı hareket ettirin ve gitmekte fayda var.

Bu çok basit bir açıklama ve bir kullanım durumudur. Bu yardımcı olur umarım!


14

Temel Fark

Muhafız bırak

  1. Kapsamdan erken var olma süreci
  2. Geri dönüş, Atma vb. Gibi mevcut puanı gerektirir.
  3. Kapsamın dışında erişilebilecek yeni bir değişken oluşturun.

izin verirsen

  1. Kapsama erişilemiyor.
  2. ifade döndürmeye gerek yok. Ama yazabiliriz

NOT: Her ikisi de İsteğe bağlı değişkeni açmak için kullanılır.


2

Gördüğüm en net açıklama Github Swift Style Guide'da :

if bir derinlik seviyesi ekler:

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

guard yapmaz:

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

2

bekçi

  • Bir guardifade, bir veya daha fazla koşul karşılanmazsa, program kontrolünü kapsam dışına aktarmak için kullanılır.

  • Bir ifadedeki herhangi bir koşulun değeri guardtipte Bool veya köprülü tipte olmalıdır Bool. Koşul, isteğe bağlı bir bağlayıcı beyan da olabilir.

Bir koruma ifadesi aşağıdaki biçime sahiptir:

guard condition else {
    //Generally return
}

izin verirsen

if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}

1

Bunu Bob ile çabucak öğrendim ..

Tipik 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")
          }
     }

Else-If ile ilgili sorunlar

  1. İç içe köşeli parantezler
  2. Hata mesajını tespit etmek için her satırı okumak zorunda

Koruma İfadesi Bir koruma bloğu yalnızca koşul yanlışsa çalışır ve dönüş yoluyla işlevden çıkar. Koşul doğruysa, Swift koruma bloğunu görmezden gelir. Erken çıkış ve daha az parantez sağlar. +

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.
       }

Else-If ile Opsiyonları Açın

Bir koruma ifadesi, tipik bir koşullu bloğu bir else-if ifadesiyle değiştirmek için yararlı olmakla kalmaz, aynı zamanda parantez sayısını en aza indirerek seçeneklerin sarmalanması için de harikadır. Karşılaştırmak için, önce else-if ile birden fazla seçeneğin nasıl açılacağına başlayalım. İlk olarak, açılmayacak üç seçenek oluşturalım.

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

En Kötü Kabus

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")
                         }
                  }

Yukarıdaki kod kesinlikle işe yarıyor ancak KURU ilkesini ihlal ediyor. Bu iğrenç. Onu parçalayalım. +

Biraz Daha İyi Aşağıdaki kod yukarıdakinden daha okunabilir. +

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
                           }
     }

Guard ile Açmak else-if ifadeleri guard ile değiştirilebilir. +

 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)
         }

Pek Çok Seçeneği Başka Değilse ile Açın Şimdiye kadar, seçenekleri tek tek açtınız. Swift, birden fazla seçeneği aynı anda açmamıza izin veriyor. Bunlardan biri nil içeriyorsa, else bloğunu çalıştıracaktır.

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")
  }
}

Birden fazla seçeneği aynı anda açtığınızda, hangisinin nil içerdiğini belirleyemeyeceğinizi unutmayın.

Guard ile Çoklu İsteğe Bağlı Birimleri Açın Elbette, else-if yerine guard kullanmalıyız. +

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
}

lütfen geri dönün ve kod biçimlendirmenizi / girintinizi düzeltin!
pkamb
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.