Swiftの「ラップされていない値」とは何ですか?


155

このチュートリアルに従って、iOS 8 / OSX 10.10のSwiftを学習しています。この段落(オブジェクトとクラス)のように、「アンラップされた値」という用語が数回使用されています

オプションの値を使用する場合は、?メソッド、プロパティ、添え字などの操作の前。の前の値なら?はnilです。は無視され、式全体の値はnilです。それ以外の場合、オプションの値はラップされていません。ラップされていない値に作用します。どちらの場合も、式全体の値はオプションの値です。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

うまくいきません。運が悪ければWebで検索しました。

これはどういう意味ですか?


編集する

Cezaryの答えから、元のコードの出力と最終的な解決策(遊び場でテストされた)の間にはわずかな違いがあります:

元のコード

元のコード

セザリーのソリューション

セザリーのソリューション

2番目のケースではスーパークラスのプロパティが出力に表示されますが、最初のケースでは空のオブジェクトがあります。

結果はどちらの場合も同じであるはずではありませんか?

関連Q&A:Swiftのオプション値は何ですか?


1
セザリーの答えはその場にあります。また、iBooks
Daniel Storm

@DanielStormAppsこのiBookは、私の質問のリンクされたチュートリアルのポータブルバージョンです:)
Bigood

回答:


289

まず、オプションタイプとは何かを理解する必要があります。オプションのタイプは、基本的に変数がであることを 意味しますnil

例:

var canBeNil : Int? = 4
canBeNil = nil

疑問符は、canBeNil可能性のある事実を示しますnil

これは機能しません:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

オプションである場合に変数から値を取得するには、ラップ解除する必要があります。これは、最後に感嘆符を付けることを意味します。

var canBeNil : Int? = 4
println(canBeNil!)

コードは次のようになります。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

補足:

疑問符の代わりに感嘆符を使用して、自動的にアンラップするオプションを宣言することもできます。

例:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

したがって、コードを修正する別の方法は次のとおりです。

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

編集:

あなたが見ている違いは、オプションの値がラップされているという事実の徴候です。その上に別のレイヤーがあります。開封されたそれは、まあ、開封されているので、バージョンがちょうどまっすぐオブジェクトを示しています。

簡単な遊び場の比較:

遊び場

最初と2番目のケースでは、オブジェクトは自動的にアンラップされないため、2つの「レイヤー」({{...}})が表示されます。3番目のケースで{...}は、オブジェクトが自動的にアンラップされるため、1つのレイヤー()のみが表示されます。

最初のケースと2番目の2つのケースの違いは、にoptionalSquare設定されてnilいる場合、2番目の2つのケースでランタイムエラーが発生することです。最初のケースの構文を使用すると、次のようなことができます。

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}

1
明確な説明をありがとう!私の質問に含まれているコードは、Appleのチュートリアル(質問のリンク)から引用されています。ただし、最終的なソリューションと元のソリューションは異なる結果を出力します(私の編集を参照)。何か考えは?
Bigood 2014年

2
涼しい。素晴らしい説明。しかし、私はまだ混乱しています。オプションの値を使用したいのはなぜですか?いつ役立つのですか?なぜAppleはこの動作と構文を作成したのですか?
トッドリーマン2014年

1
これは、クラスプロパティに関しては特に重要です。Swiftでは、オプションではないすべてのメソッドをinit()クラスの関数で初期化する必要があります。AppleがリリースしたSwiftブックの「基本」(オプションをカバー)、「初期化」、および「オプションチェーン」の章を読むことをお勧めします。
Cezary Wojcik 2014年

2
@ToddLehman Appleは、より安全なコードを記述できるようにこれを作成しました。たとえば、誰かがnullをチェックするのを忘れたためにJavaでプログラムがクラッシュするすべてのnullポインタ例外を想像してみてください。または、nilをチェックするのを忘れたため、Objective-Cでの奇妙な動作。Optionalタイプを使用すると、nilを処理することを忘れることはできません。コンパイラーは、それを処理するように強制します。
Erik Engheim 2014年

5
@AdamSmith — Javaのバックグラウンドから来て、私はオプションの使用を確実に見ることができます...しかし、(より最近では)Objective-Cのバックグラウンドからも来ています。 Cでは明示的にnilオブジェクトにメッセージを送信できます。うーん。これらの例を使って、コードを使用しない同等のコードよりも短くて安全なコードを作成する方法を誰かに紹介してもらいたいです。役に立たないと言っているのではありません。私はまだ「理解していません」と言っているだけです。
トッドリーマン

17

既存の正解は素晴らしいですが、これを完全に理解するには、これは非常に抽象的な奇妙な概念であるため、良いアナロジーが必要であることがわかりました。

ですから、正しい答えに加えて別の視点を与えることで、仲間の「右脳」(視覚的思考)開発者を助けましょう。ここに私を大いに助けた良いアナロジーがあります。

誕生日プレゼントのラッピングのアナロジー

オプションは、固く、硬く、色のついたラッピングで入ってくる誕生日プレゼントのようなものだと考えてください。

プレゼントを開けるまで、ラッピングの中に何かがあるかどうかはわかりません。おそらく何も入っていないのでしょう!内部に何かがある場合、それはまた別のプレゼントである可能性があり、これもラップされていて、含まれていない可能性があります。100個のネストされたプレゼントをアンラップして、最終的にラッピング以外に何もないことを発見することもできます。

オプションの値がそうでない場合はnil何かを含むボックスが表示されています。ただし、特に値が明示的に入力されておらず、変数であり、事前定義された定数ではない場合は、ボックスの内容、たとえば、タイプタイプなどを特定する前に、ボックス開く必要がある場合があります。実際のです。

箱の中は何ですか?!類推

変数をアンラップした後でも、SE7EN最後のシーンのブラッドピット(警告:ネタバレ、非常にRランクの悪質な言語および暴力)のようです。現在をアンラップした後でも、次の状況にあるからです。を持っているnilか、何かが入っいる箱があります(しかし、何がわからないのでしょう)。

何かのタイプを知っているかもしれません。たとえば、変数を型として宣言した場合[Int:Any?]、古い型のラップされたコンテンツを生成する整数の添え字を持つ(潜在的に空の)辞書があることがわかります。

そのため、Swiftでコレクション型(辞書と配列)を扱うと、ややこしくなることがあります。

適例:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)

素敵な😊😊😊😊😊😊😊😊😊
SamSol

アナロジーが大好きです!では、箱を開梱するより良い方法はありますか?コード例
David T.

シュレーディンガーの猫は箱に入っています
ジャクソンクル

私を混乱させてくれてありがとう....とにかく空っぽの誕生日プレゼントを贈るプログラマの種類は?ちょっとした意味
delta2flat

@Jacksonkrかどうか。
amsmath

13

Swiftは型の安全性を重視しています。Swift言語全体は、安全性を考慮して設計されています。これはSwiftの特徴の1つであり、両手を広げて歓迎すべきものです。クリーンで読みやすいコードの開発を支援し、アプリケーションがクラッシュしないようにします。

Swiftのすべてのオプションは、?記号で区切られています。?オプションとして宣言する型の名前の後にを設定することにより、これを本質的にの前にある型としてではなく、オプションの型としてキャスト?ます


注:変数または型Intはと同じではありませんInt?。これらは、互いに操作できない2つの異なるタイプです。


オプションの使用

var myString: String?

myString = "foobar"

これは、のタイプで作業しているという意味ではありませString。これは、String?(String Optional、またはOptional String)のタイプで作業していることを意味します。実際、あなたがしようとするたびに

print(myString)

実行時に、デバッグコンソールはを出力しますOptional("foobar")。" Optional()"の部分は、この変数が実行時に値を持つ場合と持たない場合があることを示していますが、現在はたまたま文字列 "foobar"が含まれているだけです。この「Optional()」の表示は、オプションの値の「アンラップ」と呼ばれる処理を行わない限り、残ります。

オプションをアンラップするということは、そのタイプを非オプションとしてキャストしていることを意味します。これにより、新しいタイプが生成され、そのオプション内に存在していた値が新しい非オプションタイプに割り当てられます。この方法では、コンパイラーによって確実な値を持つことが保証されているため、その変数に対して操作を実行できます。


条件付きアンラップは、オプションの値がであるかどうかをチェックしますnil。でない場合nil、値が割り当てられ、オプションではない定数にラップ解除される、新しく作成された定数変数があります。そして、そこから、ifブロック内の非オプションを安全に使用できます。

注:条件付きでアンラップされた定数に、アンラップするオプション変数と同じ名前を付けることができます。

if let myString = myString {
    print(myString)
    // will print "foobar"
}

オプションの条件付きアンラップは、オプションの値にアクセスするための最もクリーンな方法です。オプションにnil値が含まれている場合、if letブロック内のすべてが実行されないためです。もちろん、他のifステートメントと同様に、elseブロックを含めることができます

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

強制アンラップは、!(「強打」)演算子として知られているものを使用することによって行われます。これは安全ではありませんが、コードをコンパイルできます。ただし、bang演算子を使用する場合は常に、強制的にアンラップする前に、変数に実際に確実な値が含まれていることを1000%確認する必要があります。

var myString: String?

myString = "foobar"

print(myString!)

上記は完全に有効なSwiftコードです。myString「foobar」として設定された値を出力します。ユーザーはfoobarコンソールに印刷されているのを見るでしょう、それはそれについてです。しかし、値が設定されていなかったとしましょう:

var myString: String?

print(myString!) 

今、私たちは手に別の状況を持っています。Objective-Cとは異なり、オプションを強制的にアンラップしようとすると、オプションが設定されておらずnil、オプションをアンラップしようとすると、アプリケーションの内部がクラッシュします。


タイプキャストを使用してアンラップします。すでに述べたように、あなたがunwrappingオプションである間、実際には非オプションの型にキャストしていますが、非オプションを別の型にキャストすることもできます。例えば:

var something: Any?

コードのどこかに、変数somethingに何らかの値が設定されます。たぶん私たちはジェネリックを使用しているのかもしれませんし、多分これを変更させる他のロジックが進行中なのかもしれません。したがって、コードの後半で使用したいのですsomethingが、それが別のタイプの場合でも別の方法で処理することができます。この場合、asキーワードを使用してこれを決定する必要があります。

注:as演算子は、Swiftでキャストを入力する方法です。

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

2つのasキーワードの違いに注意してください。前と同じように、オプションを強制的にアンラップしたときは、!bang演算子を使用してアンラップしました。ここでは同じことを行いますが、単なる非オプションとしてキャストする代わりに、としてもキャストしますInt。そして、それとしてダウンキャストできなければなりませんInt。そうでなければ、値がnilアプリケーションであるときにbang演算子を使用するとクラッシュします。

また、これらの変数を何らかのソートまたは数学演算で使用するには、ラップ解除する必要があります。

たとえば、Swiftでは、同じ種類の有効な数値データタイプのみを相互に操作できます。を使用して型をキャストすると、as!その変数がその型であることが確実あるかのように、その変数のダウンキャストが強制されるため、操作しても安全で、アプリケーションをクラッシュさせません。これは、変数が実際にキャスト先の型である限り問題ありません。そうでなければ、手に混乱が生じます。

それでも、withingをas!使用すると、コードをコンパイルできます。とキャストすることas?は別の話です。実際、は完全に異なるデータ型としてas?宣言しますInt

今です Optional(0)

宿題を書こうとした場合

1 + Optional(1) = 2

あなたの数学の先生はおそらくあなたに「F」を与えたでしょう。Swiftも同様です。ただし、Swiftはグレードを付けるのではなく、まったくコンパイルしません。結局のところ、オプションは実際にはnilになる可能性があるためです。

安全第一キッズ。


オプションのタイプの素晴らしい説明。私のために物事を明確にするのに役立ちました。
Tobe_Sta

0

「?」オプションの連鎖式を意味します

。 '!'は力の値を意味します
ここに画像の説明を入力してください

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