web-dev-qa-db-ja.com

Swiftのガードキーワード

Swift 2ではguardキーワードが導入されました。これはさまざまなデータをすぐに使えるように設定するために使用できます。 this website で見た例は、submitTapped関数を示しています。

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

    print("All good")
}

私はguardを使うことがif条件を使ってそれを昔ながらのやり方でやることと少し違うのではないかと思います。それはあなたが単純な小切手を使うことによって得ることができなかった利益を与えますか?

187
David Snabel

この記事を読むGuardを使って大きな利点に気づいたGuard

ここで、guardの使用と例を比較できます。

これは無防備な部分です:

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

    // Value requirements not met, do something
}
  1. ここであなたはあなたの望むコードをすべての条件の中に置いています

    すぐにこの問題に気付くことはないかもしれませんが、ステートメントを実行する前にすべての条件を満たす必要がある多数の条件がネストされていると、混乱を招く可能性があります。

この問題を解決するには、最初に各チェックを行い、いずれかが満たされていない場合は終了します。これにより、どのような条件でこの関数が終了するかを簡単に理解できます。

しかし、これでガードを使用できるようになり、いくつかの問題を解決することが可能になりました。

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. あなたが望まない状態ではなく、あなたが望む状態をチェックすること。これもアサートに似ています。条件が満たされない場合、guardのelseステートメントが実行され、それは関数から抜け出します。
  2. 条件が満たされると、guardステートメントが呼び出された範囲内(この場合はfooGuard(_ :)関数)で、オプションの変数が自動的にラップ解除されます。
  3. あなたは早めに悪いケースをチェックしているので、あなたの関数はもっと読みやすく保守しやすくなっています。

これと同じパターンが、オプションではない値にも当てはまります。

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
}

それでも質問がある場合は、記事全体を読むことができます。 Swift guardステートメント。

まとめ

そして最後に、読んだりテストしたりして、オプションをアンラップするために保護を使用しているのであれば、

ラップされていない値は、コードブロックの残りの部分で使用するためにそのまま残ります。

guard let unwrappedName = userName else {
    return
}

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

ここでラップされていない値は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)")
358
Jorge Casariego

ifとは異なり、guardはそのブロックの外側からアクセスできる変数を作成します。たくさんのOptionalを展開するのに便利です。

35
takebayashi

guardには2つの大きな利点があります。他の人が言ったように、Doomのピラミッドを避けることができます。たくさんの厄介なif letステートメントがお互いの内側にネストされ、さらに右に移動します。

もう1つの利点は、実装したいロジックが「if not let」よりも「if let { } else」であることです。

例を示します。accumulate - mapreduceの間のクロスで、runningreduceの配列が返されるとします。これは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(+)  // []

guardなしでどうやって書くのでしょうが、それでもオプションを返すfirstを使用しますか?このようなもの:

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

}

余分なネストは面倒ですが、ifelseをそれほど離れているのも論理的ではありません。空のケースを早めに終了して、それが可能性ではなかったかのように残りの関数を続行する方がはるかに読みやすくなります。

23

guardを使用して条件が満たされると、guardブロック内で宣言された変数がコードブロックの残りの部分に公開され、それらがそのスコープに含まれます。前述のように、これはネストされたif letステートメントで確実に役立ちます。

Guardは、elseステートメントでreturnまたはthrowを必要とすることに注意してください。

GuardによるJSONの解析

以下は、if-letではなくguardを使用してJSONオブジェクトを解析する方法の例です。これはあなたがここで見つけることができる遊び場ファイルを含むブログエントリからの抜粋です:

Swift 2でGuardを使用して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)

}

ダウンロード遊び場: 保護遊び場

より詳しい情報:

これは The Swift Programming Language Guideからの抜粋です。

保護ステートメントの条件が満たされている場合、コードの実行は保護ステートメントの右中括弧の後に続きます。条件の一部としてオプションのバインディングを使用して値が割り当てられた変数または定数は、保護ステートメントが現れるコードブロックの残りの部分で使用できます。

その条件が満たされていない場合は、else分岐内のコードが実行されます。その分岐は、その保護ステートメントが含まれているコードブロックを終了するために制御を転送する必要があります。return、break、continueなどの制御転送ステートメントを使用してこれを実行できます。 fatalError()として。

19
Dan Beaulieu

1つの利点は、ネストされたif letステートメントの多くを排除することです。 15:30頃のWWDCの「新機能」のビデオ、「Doomのピラミッド」のセクションを参照してください。

7
zaph

ガードステートメントはやるつもりです。それは異なるカップルです

1)if文のネストを減らすことができます
2)私の変数がアクセスできるのは私の範囲を広げることです

if文

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 
}

保護ステートメント

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

}
5
Nazmul Hasan

ガードを使用する場合

いくつかのUITextField要素またはその他の種類のユーザー入力を含むView Controllerを持っている場合は、すぐにtextField.textオプションをアンラップして内部のテキストにアクセスする必要があることに気付くでしょう。 isEmptyはここでは何もしません。何も入力しないとテキストフィールドは単にnilを返します。

そのため、これらのうちのいくつかをアンラップし、最終的にそれらをサーバーエンドポイントにポストする関数に渡します。サーバーコードがnil値を処理したり、誤って無効な値をサーバーに送信したりする必要がないようにするため、最初にこれらの入力値を保護する必要があります。

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

あなたは私たちのサーバー通信機能がパラメータとしてオプションではない文字列値をとることに気づくでしょう。ブロック内で使用する値のラップを解除する場合は、ラップ解除で慣れているため、ラップ解除は少し直感的ではありません。ここでguardステートメントは関連ブロックを持っていますが、実際にはelseブロックです。つまり、展開が失敗した場合に行うことです。値はステートメント自体と同じコンテキストに展開されます。

// 関心事の分離

ガードなし

ガードを使用しないと、Doomのピラミッドのようなコードが山積みになってしまいます。これは、フォームに新しいフィールドを追加したり、非常に読みやすいコードを作成したりするのには適していません。特に各フォークで他にもたくさんのステートメントがある場合、インデントは従うのが難しい場合があります。

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

はい、ステートメントをカンマで区切った1つのステートメントにまとめれば、これらすべてを組み合わせることもできますが、どのステートメントが失敗したのかを判断してユーザーにメッセージを表示する機能を失うことになります。

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

5
Honey

guardを使うことで私たちの意図は明らかです。その特定の条件が満たされない場合、残りのコードを実行したくありません。ここでもチェーンを拡張することができます。以下のコードを見てください。

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

アップルのドキュメントから:

保護ステートメント

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

構文:

guard condition else {
    statements
}

利点:

1. guardステートメントを使用することで、一連の要件を検証することのみを目的とした、深くネストされた条件文を削除できます。

2.メソッドや機能を早期に終了するために特別に設計されました。

あなたが使っているなら以下のコードがどのように見えるかというコードです。

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

1つ以上の条件が満たされない場合は、guardを使用して制御をスコープ外に移すことができます。

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

参照:

1. Swift 2:警備員付きで早めに退出 2. 不幸 3. 保護声明

3
Ashok R

If文と同様に、guardは式のブール値に基づいて文を実行します。 ifステートメントとは異なり、guardステートメントは条件が満たされない場合にのみ実行されます。あなたはアサートのようにガードを考えることができますが、クラッシュするのではなく、あなたは優雅に終了することができます。

参照してください: http://ericcerney.com/Swift-guard-statement/

2
Zgpeace

簡単に言うと、実行前にフィールドを検証する方法を提供します。読みやすさが向上するので、これは良いプログラミングスタイルです。他の言語では、このように見えるかもしれません:

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

しかし、Swiftはオプションを提供しているので、nilがその値を変数に代入するかどうかを確認することはできません。対照的に、if letは、それがnilではないことを確認し、実際の値を保持する変数を割り当てます。これがguardが登場するところです。それはあなたにオプションを使って早く終了するより簡潔な方法を与えます。

1
gunby

それは本当にいくつかのルックアップとオプションを含むシーケンスの流れをより簡潔で明確にし、入れ子になっている場合の多くを減らします。 Erica Sadunによる投稿 Ifsの置き換え を参照してください。 ....下の例では、見逃されるかもしれません。

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

それが固執するかどうか確認してください。

1
DavidS

ソース: https://thenucleargeeks.com/2019/05/09/guard-in-Swift/

例を見て分かりやすくしましょう

例1

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

上記の例では、3が2より大きく、guard else句内のステートメントがスキップされ、Trueが表示されていることがわかります。

例2

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

上記の例では、1が2より小さく、guard else句内の文が実行され、Falseが表示された後に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: "")

上記の例では、オプションを展開するためにguard letを使用しています。 getName関数で、オプションの文字列myName型の変数を定義しました。次に、変数myNameがnilであるかどうかをチェックするためにguard letを使用します。nameに割り当てられていない場合はnameが空ではないことを再度チェックします。両方の条件が満たされた場合、すなわちtrueの場合、elseブロックはスキップされ、「条件は名前で満たされました」と表示されます。

基本的に、ここでは2つのことをカンマで区切ってチェックしています。最初にアンラッピングし、オプションにして、それが条件を満たしているかどうかをチェックしています。

ここでは関数には何も渡していません。つまり空の文字列なので、Conditionはfalseです。

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

ここでは、関数に "Hello"を渡しています。出力が "Condition is met Hello"と表示されていることがわかります。

0
A H M