多くの場合、次のようなコードを記述する必要があります。
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
これは少し冗長に思えますが、!
force unwrap演算子は安全ではなく、回避するのが最善です。これを処理するより良い方法はありますか?
オプションが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
}
ただし、i
はlocalコピーになることに注意してください-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
は常に他のnil
sと等しく、_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
splitViewController
がnil
以外の場合にのみ、割り当てが行われます。
値が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を引数として渡す必要がある場合に使用します。オプションの連鎖と同様に、結果はオプションになります。
とにかくオプションが必要な場合、これは素晴らしいことです。たとえば、reduce1
はreduce
に似ていますが、最初の値をシードとして使用し、配列が空の場合にオプションを返します。次のように書くことができます(以前の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) }
しかし、今ではidx
はInt??
型で、二重オプションです。代わりに、flatMap
を使用できます。これにより、結果が単一のオプションに「フラット化」されます。
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
Swiftプログラミングの本に戻って、これらの目的を学ぶべきだと思います。!は、オプションがnilでないことを絶対に確信しているときに使用されます。これは完全に意図的なものであり、コード内のアサートが「安全ではなく、回避が最善」であるという意味で「安全ではなく、回避が最善」です。
if someOptional != nil {
someFunction(someOptional!)
}
!絶対に安全です。誤って書くなど、コードに大きな過失がない限り(バグを見つけてほしい)
if someOptional != nil {
someFunction(SomeOptional!)
}
その場合、アプリがクラッシュする可能性があるため、クラッシュの原因を調査し、バグを修正します。これがまさにクラッシュの原因です。 Swift=の1つの目標は、明らかにアプリが正常に動作することですが、Swiftはこれを強制できないため、可能な場合はアプリが正常に動作するかクラッシュすることを強制します、アプリが出荷される前にバグが削除されます。
あなたには一つの方法があります。 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."
記事全文を確認できます こちら 。
多くのことを考えて研究した後、オプションのラップを解除する最も簡単な方法を思いつきました。
新しい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
どちらの演算子を使用するかはあなたの選択です。どちらも同じ目的を果たします。
オプションのバインディングを使用できます。
var x:Int?
if let y = x {
// x was not nil, and its value is now stored in y
}
else {
// x was nil
}