web-dev-qa-db-ja.com

Swift:ガードvs if let

私はSwiftのオプションについて読んでおり、オプションが値を保持しているかどうかを確認するためにif letが使用されている例を見てきました。

ただし、Swift 2.0では、キーワードguardがほとんど使用されることがわかりました。 Swift 2.0からif letが削除されたのか、それともまだ使用できるのか疑問です。

if letを含むプログラムをguardに変更する必要がありますか?

98
lmiguelvargasf

if letおよびguard letは、類似しているが別個の目的を果たします。

guardの「その他」の場合は、現在のスコープを終了する必要があります。一般的に、それはreturnを呼び出すか、プログラムを中止する必要があることを意味します。 guardは、関数の残りのネストを必要とせずに早期に戻るために使用されます。

if letはそのスコープをネストし、特別なものを必要としません。 returnであってもなくてもかまいません。

一般に、if-letブロックが関数の残りの部分になる場合、またはそのelse句にreturnが含まれる、または中止される場合は、代わりにguardを使用する必要があります。これは多くの場合(少なくとも私の経験では)、疑わしい場合は、guardが通常より良い答えであることを意味します。しかし、if letがまだ適切な状況はたくさんあります。

136
Rob Napier

ガードは改善できますclarity

ガードを使用する場合、ガードの期待値はより高いであるため、succeedになります。スコープを終了early。配列が空であるかどうか、ファイル/イメージが存在するかどうかを監視するようにガードします。

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
}

If-letを使用して上記のコードを記述すると、読み取り開発者に50〜50のコードであることがわかります。しかし、ガードを使用する場合、コードにclarityを追加します。これは、これが95%の時間で動作すると予想されることを意味します...失敗した場合、なぜそうなるのかわかりません。非常にありそうにありません...しかし、代わりにこのデフォルトの画像を使用するか、おそらく何が間違っているかを説明する意味のあるメッセージでアサートするだけです!

  • 副作用が生じる場合はguardsを避けてください。ガードは natural フローとして使用されます。 else句が副作用を引き起こす場合は、ガードを避けてください。ガードは、 required がコードを正しく実行するための条件を確立し、 early exit を提供します

  • 正の分岐で重要な計算を実行する場合、ifからguardステートメントにリファクタリングし、else句でフォールバック値を返します。

From:Erica Sadun's Swift St​​yle book

また、上記の提案とクリーンなコードの結果として、より多くの可能性failedガードステートメントにアサーションを追加する必要があります。読みやすさが向上し、明確になります。あなたが期待していたことを他の開発者に。

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

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

From:Erica Sadun's Swift St​​yle book + some modification

if-letsにはassert/preconditionsを使用しません。正しくないようです)

ガードを使用すると、Doomのavoiding pyramidによって、明瞭さを改善するのにも役立ちます。 Nitinの回答 を参照してください。


Guardはnew変数を作成します

誰もうまく説明していないと思う重要な違いが1つあります。

guardif letの両方nwrap変数

guardを使用すると、は、elseステートメントの外にexistになる新しい変数を作成しています

if letを使用すると、新しい変数を作成しない任意の新しい変数-elseステートメントの後、あなただけenterコードブロックifオプション非ゼロです。新しく作成された変数は inside のみであり、コードブロックは後ではありません!

guard:

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

if letの詳細については、以下を参照してください: オプションのバインディングを再宣言してもエラーが発生しない理由


Guardにはscope exitingが必要です

(ロブ・ネイピアの回答でも言及されています):

guardを定義する必要がありますinside func。主な目的は、スコープを中止/返送/終了することですif条件が満たされない:

var str : String?

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

if letの場合、func内に含める必要はありません。

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

if-letをいつ使用し、guardをいつ使用するかは、スタイルの問題です。

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intとオプションのアイテムの配列(var optionalArray: [SomeType]?)があり、配列がnil(未設定)の場合は0を、配列に値がある(設定されている)場合はcountを返す必要があるとします。

if-letを使用して、次のように実装できます。

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

またはguardを使用してこのようにします:

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

例は機能的に同じです。

guardが本当に輝くのは、データの検証などのタスクがあり、何か問題がある場合に関数が早期に失敗するようにする場合です。

検証の終了に近づくにつれてif-letsの束をネストする代わりに、「成功パス」と現在正常にバインドされたオプションは、すべて失敗パスがすでに返されているため、すべてメソッドのメインスコープにあります。

45
divergio

いくつかの(最適化されていない)コードを使用して、ガードステートメントの有用性を説明しようとします。

名、姓、電子メール、電話、パスワードを使用してユーザー登録用のテキストフィールドを検証するUIがあります。

TextFieldに有効なテキストが含まれていない場合、そのフィールドをfirstResponderにする必要があります。

最適化されていないコードは次のとおりです。

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

上記のように、すべての文字列(firstNameString、lastNameStringなど)はifステートメントのスコープ内でのみアクセス可能であることがわかります。そのため、この「運命のピラミッド」が作成され、読みやすさや移動のしやすさなど、多くの問題があります(フィールドの順序が変更された場合、このコードの大部分を書き換える必要があります)

(以下のコードの)guardステートメントを使用すると、これらの文字列は{}の外部で利用可能であり、すべてのフィールドが有効な場合に利用されることがわかります。

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

フィールドの順序が変更された場合は、それぞれのコード行を上下に移動するだけでよいのです。

これは非常に簡単な説明とユースケースです。お役に立てれば!

28
Nitin Alabur

基本的な違い

ガードレット

  1. スコープからの早期存在プロセス
  2. リターン、スローなどのスコアが必要です。
  3. スコープからアクセスできる新しい変数を作成します。

させて

  1. スコープにアクセスできません。
  2. 文を返す必要はありません。しかし、私たちは書くことができます

注:オプション変数のラップ解除には両方が使用されます。

8
Kiran K

私が見た最も明確な説明は Github Swift St​​yle Guide

ifは深さのレベルを追加します。

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

guardはしません:

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

Guard

  • ガードステートメントは、1つ以上の条件が満たされない場合にプログラム制御をスコープ外に転送するために使用されます。

  • ガードステートメントの条件の値は、Bool型またはBoolにブリッジされた型である必要があります。条件は、オプションのバインディング宣言にすることもできます

ガードステートメントの形式は次のとおりです。

guard condition else {
    //Generally return
}

if let

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

これは、ボブとのSwiftから学びました。

典型的な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の問題

  1. ネストされたブラケット
  2. エラーメッセージを見つけるためにすべての行を読む必要があります

ガードステートメントガードブロックは、条件がfalseの場合にのみ実行され、リターンによって関数から終了します。条件が真の場合、Swiftはガードブロックを無視します。早期終了とより少ない括弧を提供します。+

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でオプションを展開

ガードステートメントは、典型的な条件ブロックをelse-ifステートメントで置き換えるのに役立つだけでなく、括弧の数を最小限に抑えることでオプションをアンラップするのにも最適です。比較するために、まずelse-ifで複数のオプションを展開する方法を始めましょう。最初に、ラップ解除される3つのオプションを作成します。

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

最悪の悪夢

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

上記のコードは確かに機能しますが、DRYの原則に違反しています。ひどいです。分解しましょう+。

わずかに良い以下のコードは上記よりも読みやすくなっています。+

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

ガード付きアンラップ else-ifステートメントはガードに置き換えることができます。+

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

Else-Ifで複数のオプションを展開これまで、オプションを1つずつ展開してきました。 Swiftを使用すると、複数のオプションを一度に展開できます。それらの1つがnilを含む場合、elseブロックを実行します。

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

複数のオプションを一度にアンラップすると、nilを含むものを識別できないことに注意してください

Guardで複数のオプションを展開するもちろん、else-if。+ではguardを使用する必要があります+

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
}
0
Ourang-Zeb Khan