web-dev-qa-db-ja.com

オプションの値をnilと比較する必要があるのはいつですか?

多くの場合、次のようなコードを記述する必要があります。

if someOptional != nil {
    // do something with the unwrapped someOptional e.g.       
    someFunction(someOptional!)
}

これは少し冗長に思えますが、! force unwrap演算子は安全ではなく、回避するのが最善です。これを処理するより良い方法はありますか?

43

オプションがnilではないかどうかを確認することはほとんど常に不要です。これを行う必要があるのは、ほとんどの場合、そのnil- nessがonlyについて知りたい場合です–あなた値が何であるかは気にせず、単にnilではないというだけです。

他のほとんどの状況では、if内でより安全かつ簡潔にタスクを実行できるSwift略記法)が少しあります。

nilでない場合は値を使用

の代わりに:

let s = "1"
let i = Int(s)

if i != nil {
    print(i! + 1)
}

if letを使用できます:

if let i = Int(s) {
    print(i + 1)
}

varを使用することもできます:

if var i = Int(s) {
    print(++i)  // prints 2
}

ただし、ilocalコピーになることに注意してください-iを変更しても元の値には影響しませんオプション。

単一のif let内で複数のオプションをアンラップでき、後のものは前のものに依存できます。

if let url = NSURL(string: urlString),
       data = NSData(contentsOfURL: url),
       image = UIImage(data: data)
{
    let view = UIImageView(image: image)
    // etc.
}

ラップされていない値にwhere句を追加することもできます。

if let url = NSURL(string: urlString) where url.pathExtension == "png",
   let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }

nilをデフォルトに置き換える

の代わりに:

let j: Int
if i != nil {
    j = i
}
else {
    j = 0
}

または:

let j = i != nil ? i! : 0

nil-coalescing演算子??を使用できます。

// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0

オプションと非オプションを同等にする

の代わりに:

if i != nil && i! == 2 {
    print("i is two and not nil")
}

オプションが非オプション値と等しいかどうかを確認できます。

if i == 2 {
    print("i is two and not nil")
}

これは比較でも機能します:

if i < 5 { }

nilは常に他のnilsと等しく、_nil以外の値よりも小さくなります。

注意してください!ここに落とし穴があります:

let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
    print("these will be equal because both nil...")
}

オプションでメソッドを呼び出す(またはプロパティを読み取る)

の代わりに:

let j: Int
if i != nil {
    j = i.successor()
}
else {
   // no reasonable action to take at this point
   fatalError("no idea what to do now...")
}

オプションの連鎖?.を使用できます:

let j = i?.successor()

jシナリオを考慮して、fatalErrorもオプションになりました。後で、この回答で他の手法のいずれかを使用してjのオプションを処理できますが、オプションのアンラップを実際に延期するか、またはまったくしない場合があります。

名前が示すように、それらを連鎖させることができるので、次のように書くことができます。

let j = s.toInt()?.successor()?.successor()

オプションの連鎖は、添え字でも機能します。

let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7]  // returns {Some 7}

および機能:

let dictOfFuncs: [String:(Int,Int)->Int] = [
      "add":(+),
      "subtract":(-)
]

dictOfFuncs["add"]?(1,1)  // returns {Some 2}

オプションのプロパティへの割り当て

の代わりに:

if splitViewController != nil {
    splitViewController!.delegate = self 
}

オプションのチェーンをthroughに割り当てることができます:

splitViewController?.delegate = self

splitViewControllernil以外の場合にのみ、割り当てが行われます。

値がnilでない場合、またはベイリング(Swift 2.0)の新機能)

関数には、オプションをチェックするための短いコードが必要な場合があります。それがnilの場合は、関数を早期に終了するか、それ以外の場合は続行します。

次のように記述できます。

func f(s: String) {
    let i = Int(s)
    if i == nil { fatalError("Input must be a number") }
    print(i! + 1)
}

または、次のように強制的に展開されるのを避けるには:

func f(s: String) {
    if let i = Int(s) {
        print(i! + 1)
    }
    else { 
        fatalErrr("Input must be a number")
    }
}

ただし、チェックによってエラー処理コードを最上部に保持する方がはるかに優れています。これはまた、不快なネスト(「運命のピラミッド」)につながる可能性があります。

代わりに、if not letのようなguardを使用できます。

func f(s: String) {
    guard let i = Int(s)
        else { fatalError("Input must be a number") }

    // i will be an non-optional Int
    print(i+1)
}

else部分mustは、保護された値のスコープを終了します。 returnまたはfatalError。保護された値がスコープの残りの部分で有効であることを保証します。

guardは関数スコープに限定されません。たとえば、次のとおりです。

var a = ["0","1","foo","2"]
while !a.isEmpty  {
    guard let i = Int(a.removeLast())
        else { continue }

    print(i+1, appendNewline: false)
}

321を出力します。

シーケンス内の非nilアイテムのループ(Swift 2.0)の新機能)

オプションのシーケンスがある場合、for case let _?を使用して、すべての非オプション要素を反復処理できます。

let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
    print(i+1, appendNewline: false)
}

321を出力します。これは、オプションのパターン一致構文を使用しています。これは、変数名の後に?が続きます。

switchステートメントでこのパターンマッチングを使用することもできます。

func add(i: Int?, _ j: Int?) -> Int? {
    switch (i,j) {
    case (nil,nil), (_?,nil), (nil,_?):
        return nil
    case let (x?,y?):
        return x + y
    }
}

add(1,2)    // 3
add(nil, 1) // nil

関数がnilを返すまでループする

if letと同様に、while letを記述し、nilまでループすることもできます。

while let line = readLine() {
    print(line)
}

while varと書くこともできます(if varと同様の注意事項が適用されます)。

where句もここで機能します(スキップするのではなく、ループを終了します)。

while let line = readLine() 
where !line.isEmpty {
    print(line)
}

非オプションを取り、結果を返す関数にオプションを渡す

の代わりに:

let j: Int
if i != nil {
    j = abs(i!)
}
else {
   // no reasonable action to take at this point
   fatalError("no idea what to do now...")
}

オプションのmap演算子を使用できます。

let j = i.map { abs($0) }

これはオプションの連鎖に非常に似ていますが、オプションではない値intoを引数として渡す必要がある場合に使用します。オプションの連鎖と同様に、結果はオプションになります。

とにかくオプションが必要な場合、これは素晴らしいことです。たとえば、reduce1reduceに似ていますが、最初の値をシードとして使用し、配列が空の場合にオプションを返します。次のように書くことができます(以前のguardキーワードを使用):

extension Array {
    func reduce1(combine: (T,T)->T)->T? {

        guard let head = self.first
            else { return nil }

        return dropFirst(self).reduce(head, combine: combine)
    }
}

[1,2,3].reduce1(+) // returns 6

しかし、代わりにmap.firstプロパティを使用して、それを返すことができます:

extension Array {
    func reduce1(combine: (T,T)->T)->T? {
        return self.first.map {
            dropFirst(self).reduce($0, combine: combine)
        }
    }
}

オプションを受け取り、結果を返す関数にオプションを渡すことで、煩わしい二重オプションを回避します

mapに似たものが必要な場合もありますが、呼び出したい関数itselfはオプションを返します。例えば:

// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first  // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }

しかし、今ではidxInt??型で、二重オプションです。代わりに、flatMapを使用できます。これにより、結果が単一のオプションに「フラット化」されます。

let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int? 
// and not Int?? unlike if `map` was used
109

Swiftプログラミングの本に戻って、これらの目的を学ぶべきだと思います。!は、オプションがnilでないことを絶対に確信しているときに使用されます。これは完全に意図的なものであり、コード内のアサートが「安全ではなく、回避が最善」であるという意味で「安全ではなく、回避が最善」です。

if someOptional != nil {
    someFunction(someOptional!)
}

!絶対に安全です。誤って書くなど、コードに大きな過失がない限り(バグを見つけてほしい)

if someOptional != nil {
    someFunction(SomeOptional!)
}

その場合、アプリがクラッシュする可能性があるため、クラッシュの原因を調査し、バグを修正します。これがまさにクラッシュの原因です。 Swift=の1つの目標は、明らかにアプリが正常に動作することですが、Swiftはこれを強制できないため、可能な場合はアプリが正常に動作するかクラッシュすることを強制します、アプリが出荷される前にバグが削除されます。

2
gnasher729

あなたには一つの方法があります。 Optional Chaining と呼ばれます。ドキュメントから:

オプションの連鎖とは、現在nilである可能性のあるオプションのプロパティ、メソッド、およびサブスクリプトを照会および呼び出すプロセスです。オプションに値が含まれている場合、プロパティ、メソッド、または添え字の呼び出しは成功します。オプションがnilの場合、プロパティ、メソッド、または添え字呼び出しはnilを返します。複数のクエリをチェーン化でき、チェーン内のリンクがnilの場合、チェーン全体が正常に失敗します。

ここにいくつかの例があります

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

if let roomCount = john.residence?.numberOfRooms {
    println("John's residence has \(roomCount) room(s).")
} else {
    println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."

記事全文を確認できます こちら

0
Sergey Pekar

多くのことを考えて研究した後、オプションのラップを解除する最も簡単な方法を思いつきました。

  • 新しいSwiftファイルを作成し、UnwrapOperator.Swiftという名前を付けます

  • 次のコードをファイルに貼り付けます:

    import Foundation
    import UIKit
    
    protocol OptionalType { init() }
    
    extension String: OptionalType {}
    extension Int: OptionalType {}
    extension Int64: OptionalType {}
    extension Float: OptionalType {}
    extension Double: OptionalType {}
    extension CGFloat: OptionalType {}
    extension Bool: OptionalType {}
    extension UIImage : OptionalType {}
    extension IndexPath : OptionalType {}
    extension NSNumber : OptionalType {}
    extension Date : OptionalType {}
    extension UIViewController : OptionalType {}
    
    postfix operator *?
    postfix func *?<T: OptionalType>( lhs: T?) -> T {
    
        guard let validLhs = lhs else { return T() }
        return validLhs
    }
    
    prefix operator /
    prefix func /<T: OptionalType>( rhs: T?) -> T {
    
        guard let validRhs = rhs else { return T() }
        return validRhs
    }
    
  • これで、上記のコードは2つの演算子[1つのプレフィックスと1つのポストフィックス]を作成しました。

  • アンラップ時には、これらの演算子のいずれかをオプションの前または後に使用できます
  • 説明は簡単です。演算子は、変数にnilが含まれる場合はconstructor valueを返し、それ以外の場合は変数内に含まれる値を返します。

  • 以下に使用例を示します。

    var a_optional : String? = "abc"
    var b_optional : Int? = 123
    
    // before the usage of Operators
    
    print(a_optional) --> Optional("abc")
    print(b_optional) --> Optional(123)
    
    // Prefix Operator Usage
    
    print(/a_optional) --> "abc"
    print(/b_optional) --> 123
    
    // Postfix Operator Usage
    
    print(a_optional*?) --> "abc"
    print(b_optional*?) --> 123
    
  • 変数にnilが含まれるの場合の例を以下に示します。

    var a_optional : String? = nil
    var b_optional : Int? = nil
    
    // before the usage of Operators
    
    print(a_optional) --> nil
    print(b_optional) --> nil
    
    // Prefix Operator Usage
    
    print(/a_optional) --> ""
    print(/b_optional) --> 0
    
    // Postfix Operator Usage
    
    print(a_optional*?) --> ""
    print(b_optional*?) --> 0
    
  • どちらの演算子を使用するかはあなたの選択です。どちらも同じ目的を果たします。

0
Mr. Bean

オプションのバインディングを使用できます。

var x:Int?

if let y = x {
  // x was not nil, and its value is now stored in y
}
else {
  // x was nil
}
0
13th Ghost