Swiftのguardキーワード


197

Swift 2で導入されたguardキーワードは、さまざまなデータをすぐに構成できるようにするために使用できます。このWebサイトで見た例は、submitTapped関数を示しています。

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

    print("All good")
}

条件guardを使用して、昔ながらの方法を使用することと何が違うのかと思いifます。簡単なチェックでは得られなかったメリットはありますか?


また、ガードvs if-letの質問も参照してください
Honey

次のリンクを参照してください。medium.com/ @ pvpriya7 / swift
Priyanka V

回答:


368

この記事を読んで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 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)")

3
@Ericさん、すばらしい投稿をしました!わかりやすくしてくれてありがとう!
ホルヘカサリエゴ2015年

1
NSErrorのアンラップにガードを使用しています。しかし、(たとえば、コールバックにエラーを渡すために)ガードスコープ内で使用しようとすると、「ガード条件で宣言された変数は本体で使用できません」と表示されます。それは意味がありますか?ありがとう
GeRyCh

6
@GeRyChがガードステートメントでアンラップすると、その変数はガードステートメント内ではなく、ガードステートメントの後で使用できるようになります。これに慣れるまでしばらくかかりました。
DonnaLea

2
ガードを使用してオプションをきれいにアンラップする別の優れた記事を次に示します。うまくまとめます。
Doches

let x = x where x > 0あなたがいる意味が連結され、あなたのオプションの結合に別の条件を?私はそれが少し異なります意味if let constantName = someOptional { statements }
ハニー

36

とは異なりifguardブロックの外部からアクセスできる変数を作成します。たくさんのOptionals を展開すると便利です。


24

には、2つの大きなメリットがありguardます。1つは、他の人が述べたように、運命のピラミッドを回避することif letです。相互に入れ子になっている多くの迷惑なステートメントがさらに右に移動します。

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

ここでは例です:あなたが実装したいとaccumulate-間のクロスmapreduce、それはあなたがの配列バックできます実行しているが、減少しています。ここにあり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(+)  // []

どのようにガードなしでそれを書くでしょうが、それでもそれを使用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 []
        }
    }

}

入れ子を増やすのは面倒ですが、ifとをelseこれほど離れて配置するのはそれほど論理的ではありません。空のケースを早期に終了し、それが可能ではなかったかのように関数の残りの部分を続行する方がはるかに読みやすくなります。


19

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

ガードは、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()として。


7

1つの利点は、ネストされたif letステートメントの多くを排除することです。15:30頃のWWDC「What's New in Swift」のビデオ、「Pyramid of Doom」というタイトルのセクションを参照してください。


6

ガードを使用する場合

いくつかのUITextField要素またはその他のタイプのユーザー入力を含むビューコントローラーがある場合は、オプションで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) {
  ...
}

サーバー通信関数はオプションの文字列値をパラメーターとして取るので、ガードが事前にアンラップしていることに気づくでしょう。ブロック内で使用するために値をアンラップするif letでアンラップすることに慣れているため、アンラップは少し直感的ではありません。ここで、guardステートメントには関連付けられたブロックがありますが、それは実際にはelseブロックです。つまり、アンラップが失敗した場合に行うことです。値は、ステートメント自体と同じコンテキストに直接アンラップされます。

// 関心事の分離

ガードなし

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

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

はい、これらのletステートメントをすべてコンマで区切った単一のステートメントに結合することもできますが、失敗したステートメントを特定してユーザーにメッセージを表示する機能を失うことになります。

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


5

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

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

5

ガードステートメントはやるつもりです。いくつか違います

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

}

3

Appleのドキュメントから:

ガードステートメント

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

Synatx:

guard condition else {
    statements
}

利点:

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

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

if letを使用する場合は、以下のコードを見てください。

  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つ以上の条件が満たされない場合にスコープ外に制御を移すことができます。

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. スイフト2:ガードを使用して早期終了 2. Udacity 3. ガードの声明


しかし、ifステートメントで後者を実行できますか? if condition { return }ばか?
Oliver Dixon

2

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

参照:http : //ericcerney.com/swift-guard-statement/


1

これは本当に、いくつかのルックアップとオプションのシーケンスのフローをより簡潔で明確にし、ネストされている場合の多くを減らします。Ifsの置き換えに関する Erica Sadunの投稿参照してください。....以下の例に夢中になります:

    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

簡単に言えば、実行前にフィールドを検証する方法を提供します。これは読みやすさを向上させるため、優れたプログラミングスタイルです。他の言語では、次のようになります。

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

スウィフトはoptionalsを提供しますので、しかし、我々はそれがnilだかどうかを確認することができない、変数に値を割り当てます。対照的に、nilないif letことを確認し、実際の値を保持する変数を割り当てます。これが出番です。これは、オプションを使用して早期に終了するより簡潔な方法を提供します。guard


1

ソース:ガードのスイフト

それを明確に理解するために例を見てみましょう

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

上記の例では、オプションをアンラップするためにガードレットを使用しています。関数getNameで、オプションの文字列myName型の変数を定義しました。次に、guard letを使用して、変数myNameがnilであるかどうかを確認します。名前に割り当てない場合は、名前が空でないことを再度確認します。両方の条件が当てはまる場合、つまりtrueの場合、elseブロックはスキップされ、「条件が名前と一致しています」と出力されます。

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

ここでは、関数に何も渡していない、つまり空の文字列なので、Condition is 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」という出力が表示されていることがわかります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.