私はSwiftのオプションについて読んでおり、オプションが値を保持しているかどうかを確認するためにif let
が使用されている例を見てきました。
ただし、Swift 2.0では、キーワードguard
がほとんど使用されることがわかりました。 Swift 2.0からif let
が削除されたのか、それともまだ使用できるのか疑問です。
if let
を含むプログラムをguard
に変更する必要がありますか?
if let
およびguard let
は、類似しているが別個の目的を果たします。
guard
の「その他」の場合は、現在のスコープを終了する必要があります。一般的に、それはreturn
を呼び出すか、プログラムを中止する必要があることを意味します。 guard
は、関数の残りのネストを必要とせずに早期に戻るために使用されます。
if let
はそのスコープをネストし、特別なものを必要としません。 return
であってもなくてもかまいません。
一般に、if-let
ブロックが関数の残りの部分になる場合、またはそのelse
句にreturn
が含まれる、または中止される場合は、代わりにguard
を使用する必要があります。これは多くの場合(少なくとも私の経験では)、疑わしい場合は、guard
が通常より良い答えであることを意味します。しかし、if let
がまだ適切な状況はたくさんあります。
ガードを使用する場合、ガードの期待値はより高いであるため、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%の時間で動作すると予想されることを意味します...失敗した場合、なぜそうなるのかわかりません。非常にありそうにありません...しかし、代わりにこのデフォルトの画像を使用するか、おそらく何が間違っているかを説明する意味のあるメッセージでアサートするだけです!
副作用が生じる場合は
guard
sを避けてください。ガードは natural フローとして使用されます。else
句が副作用を引き起こす場合は、ガードを避けてください。ガードは、 required がコードを正しく実行するための条件を確立し、 early exit を提供します正の分岐で重要な計算を実行する場合、
if
からguard
ステートメントにリファクタリングし、else
句でフォールバック値を返します。
また、上記の提案とクリーンなコードの結果として、より多くの可能性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 Style book + some modification
(if-let
sにはassert/preconditionsを使用しません。正しくないようです)
ガードを使用すると、Doomのavoiding pyramidによって、明瞭さを改善するのにも役立ちます。 Nitinの回答 を参照してください。
誰もうまく説明していないと思う重要な違いが1つあります。
guard
とif 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
を定義する必要があります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!
}
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-let
sの束をネストする代わりに、「成功パス」と現在正常にバインドされたオプションは、すべて失敗パスがすでに返されているため、すべてメソッドのメインスコープにあります。
いくつかの(最適化されていない)コードを使用して、ガードステートメントの有用性を説明しようとします。
名、姓、電子メール、電話、パスワードを使用してユーザー登録用のテキストフィールドを検証する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)
}
フィールドの順序が変更された場合は、それぞれのコード行を上下に移動するだけでよいのです。
これは非常に簡単な説明とユースケースです。お役に立てれば!
基本的な違い
注:オプション変数のラップ解除には両方が使用されます。
私が見た最も明確な説明は Github Swift Style Guide :
if
は深さのレベルを追加します。
if n.isNumber {
// Use n here
} else {
return
}
guard
はしません:
guard n.isNumber else {
return
}
// Use n here
ガードステートメントは、1つ以上の条件が満たされない場合にプログラム制御をスコープ外に転送するために使用されます。
ガードステートメントの条件の値は、Bool型またはBoolにブリッジされた型である必要があります。条件は、オプションのバインディング宣言にすることもできます
ガードステートメントの形式は次のとおりです。
guard condition else {
//Generally return
}
- オプションのバインディング としても人気
- オプションのオブジェクトにアクセスするには、if letを使用します
if let roomCount = optionalValue {
print("roomCount available")
} else {
print("roomCount is nil")
}
これは、ボブとの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の問題
ガードステートメントガードブロックは、条件が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
}