Swift:nilのオプションをテストする


137

私はXcode 6 Beta 4を使用しています。オプションを適切にテストする方法を理解できないという奇妙な状況があります。

オプションのxyzがある場合、テストする正しい方法です。

if (xyz) // Do something

または

if (xyz != nil) // Do something

ドキュメントは最初の方法でそれを行うと書いていますが、2番目の方法が必要な場合があり、コンパイラエラーが発生しないこともありますが、2番目の方法ではコンパイラエラーが発生することもあります。

私の具体的な例は、迅速にブリッジされるGData XMLパーサーを使用することです。

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

ここで、私がやった場合:

if xmlError

常にtrueを返します。ただし、私が行う場合:

if (xmlError != nil)

次に、(Objective-Cでの動作と同様に)動作します。

GData XMLに何かあり、それが欠けているオプションを処理する方法はありますか?


4
予期しないケースとエラーのケースの完全な例を確認できますか?(そして、私はそれが難しいことを知っていますが、条件文の前後の括弧をなくしてみてください!)
マットギブソン14

1
これはXcode 6ベータ5で変更されました
newacct 2014


予期しないケースで質問を更新しました。
2014

まだベータ5に更新していません。私はすぐにそれをします。お目にかかれて光栄です ??Swift、およびより一貫したオプションの動作。
2014

回答:


172

Xcode Beta 5では、次のことができなくなりました。

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

これによりエラーが発生します。

プロトコル「BooleanType.Protocol」に準拠していません

次のいずれかの形式を使用する必要があります。

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}

5
誰かがxyz == nilが機能しない理由について詳しく説明できますか?
クリストフ

第二の例は次のようになります//(2つの変種間のユースケースの主な違いである)のxyを使用して実行(Do)何か
アルパース

@Christoph:エラーメッセージは、初期化される前に使用される定数「xyz」です。xyzの宣言の行末に「= nil」を追加する必要があると思います。
user3207158

私のようにすぐに新しいので不思議に思っている人のために:ifブロック内でxyzをアンラップする必要があります。アンラップを強制しても安全です
Omar

@Omarこれが2番目のフォームの美点です。ブロックを展開せずにブロック内でxyを使用します。
Erik Doernenburg、

24

if条件内で別の名前の変数に割り当てる代わりに、他の回答に追加するには:

var a: Int? = 5

if let b = a {
   // do something
}

次のように同じ変数名を再利用できます。

var a: Int? = 5

if let a = a {
    // do something
}

これにより、クリエイティブ変数名が不足するのを防ぐことができます...

これは、Swiftでサポートされている可変シャドウを利用しています


18

オプションを使用する最も直接的な方法の1つは次のとおりです。

たとえばxyz、仮定はオプションのタイプですInt?

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

このようにしxyzて、値が含まれているかどうかをテストし、含まれている場合は、すぐにその値を処理できます。

コンパイラエラーに関しては、型UInt8はオプションではなく(「?」に注意)、したがってに変換できませんnil。変数を扱う前に、使用する変数がオプションであることを確認してください。


1
私は不必要に新しい変数(定数)を作成するため、この方法は悪いと思います。ほとんどの場合、以前よりも最悪になる新しい名前を作成する必要があります。書くため、そして読者のために私にもっと時間をかけます。
ロンバス

3
これは、オプション変数をアンラップするための標準的な方法であり、推奨される方法です。「もしも​​」は非常に強力です。
gnasher729 2017

@ gnasher729しかし、else部分のみを使用したい場合
Brad Thomas

15

Swift 3.0、4.0

nilのオプションをチェックする方法は主に2つあります。これらを比較した例を次に示します

1.させる場合

if letオプションのnilをチェックする最も基本的な方法です。このnilチェックに、コンマで区切って他の条件を追加できます。変数は、次の条件に移動するためにnilであってはなりません。nilチェックのみが必要な場合は、次のコードで余分な条件を削除します。

それ以外では、if xがnilでない場合、ifクロージャーが実行され、x_val内部で使用できます。そうでない場合は、elseクロージャーがトリガーされます。

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2.ガードレット

guard let同様のことができます。主な目的は、論理的により合理的にすることです。これは、変数がnilでないことを確認しますそれ以外の場合は関数を停止しますguard letとして追加の条件チェックを行うこともできif letます。

違いは、guard let以下のコメントに示すように、ラップされていない値がと同じスコープで使用できることです。これもによって、他の閉鎖で、プログラムは現在のスコープを終了していることをポイントにつながるreturnbreakなど

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope

11

迅速なプログラミングガイドから

ifステートメントと強制アンラップ

ifステートメントを使用して、オプションに値が含まれているかどうかを確認できます。オプションに値がある場合、trueと評価されます。値がない場合は、falseと評価されます。

これを行う最良の方法は

// swift > 3
if xyz != nil {}

またxyz、ifステートメントを使用している場合xyzは、定数変数でifステートメントをアンラップできます。したがって、ifステートメントxyzが使用されているすべての場所をアンラップする必要はありません。

if let yourConstant = xyz{
      //use youtConstant you do not need to unwrap `xyz`
}

この慣習はによって提案されてappleおり、開発者が従います。


2
Swift 3では、実行する必要がありますif xyz != nil {...}
psmythirl

Swift 3では、オプションはブール値として使用できません。1つ目は、そもそもこの言語に含まれるべきではなかったC-ismであり、2つ目は、オプションのBoolとの完全な混乱を引き起こすためです。
gnasher729 2017

9

オプションを明示的に比較するnilか、オプションのバインディングを使用してその値を追加で抽出する必要があります(つまり、オプションは暗黙的にブール値に変換されません)が、Swift 2が動作時に破滅ピラミッドを回避するのに役立つguardステートメントを追加したことは注目に値します複数のオプション値。

つまり、オプションには次の明示的なチェックが含まれますnil

if xyz != nil {
    // Do something with xyz
}

オプションのバインディング:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

そしてguardステートメント:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

オプションの値が複数ある場合に、通常のオプションのバインディングでインデントが大きくなることに注意してください。

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

guardステートメントでこのネストを回避できます。

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz

1
上記のように、これを行うと、ネストされたオプションの統計情報を取り除くことができます。if let abc = abc?.xyz?.something {}
Suhaib

1
@Suhaibああ、true:オプションのチェーン(developer.apple.com/library/prerelease/content/documentation/…)を使用して、ネストされたプロパティにすばやくアクセスすることもできます。元の質問からの正接が多すぎるのではないかと思ったので、ここでは触れませんでした。
Chris Frederick

正解です。しかし、Swift、OMG ...プログラマーフレンドリーになるまでにはまだ長い道のりがあります!
チューリングテスト済み

@turingtestedありがとう!実際、これ誤ってnil値を使用しないことを保証するという意味でプログラマーフレンドリーであると思いますが、学習曲線が少し急であることには同意します。:)
クリスフレデリック

4

Swift 5プロトコル拡張

オプションのnilチェックを簡単にインライン化できるように、プロトコル拡張を使用するアプローチは次のとおりです。

import Foundation

public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

使用法

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

私はこの回答に対していくつかの反対票を獲得しました。その理由を知りたいのですが。反対票を投じる場合は、少なくともコメントしてください。
Brody Robertson

1
おそらく、Pythonの純粋な形で言語を維持しようとするもののswift類似物でしょうpythonistas。私の見解?私はあなたのコードを私のUtilsミックスインに持ち上げました。
javadba

2

これで、次のことをすばやく行うことができます。これにより、目的のcを少し取り戻すことができます。 if nil else

if textfieldDate.text?.isEmpty ?? true {

}

2

の代わりにif、何かがnilであるかどうかに基づいて値を取得したい場合、三項演算子が役立つ場合があります。

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}

xyz == nil式は機能しませんが、x != nil機能します。理由がわからない。
Andrew

2

オプションのバインディングを行うためにifor guardステートメントを使用する以外の別のアプローチは、次のように拡張Optionalすることです:

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValueオプションがnilでない場合、クロージャを受け取り、その値を引数として呼び出します。これは次のように使用されます。

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

ifまたはを使用する必要guardがあります。これらはSwiftプログラマーが使用する最も一般的な(したがって、よく知られている)アプローチであるためです。


2

特に取り上げられていないオプションの1つは、Swiftの無視された値の構文を使用することです。

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

nilSwiftのような現代の言語では、チェックするのが場違いであるので、私はこれが好きです。場違いに感じられるのは、nil基本的には歩哨の価値だと思います。最近のプログラミングでは、他のほとんどすべての場所で歩哨を廃止したので、歩哨nilも行くべきだと感じています。


1

また使用できます Nil-Coalescing Operator

nil-coalescing operatora ?? b)オプションをアンラップするaことが含まれている場合aの値、または返すaデフォルト値をb場合aですnil。式aは常にオプションの型です。式bは、に格納されているタイプと一致する必要がありaます。

let value = optionalValue ?? defaultValue

場合optionalValuenil、それは自動的に値を代入しますdefaultValue


デフォルト値を指定する簡単なオプションを提供するので、私はそれが好きです。
thowa

0
var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

このテストは、すべてのケースで予想どおりに機能しました。ところで、ブラケットは必要ありません()


2
OPが懸念するケースは次のとおりif (xyz != nil)です。
zaph 2014

0

条件付きでアンラップして比較したい場合は、次のように複合ブール式の短絡評価を利用してみてはいかがでしょうか。

if xyz != nil && xyz! == "some non-nil value" {

}

確かに、これは他のいくつかの提案された投稿ほど読みやすいものではありませんが、他の提案されたソリューションよりも仕事が完了し、やや簡潔になります。

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