web-dev-qa-db-ja.com

なぜ「暗黙のうちにラップされていないオプション」を作成するのですか。

通常の変数や定数だけを作成するのではなく、「暗黙的にラップしないオプション」を作成するのはなぜですか。うまくアンラップできることがわかっているなら、そもそもなぜオプションを作成するのでしょうか。たとえば、これはなぜでしょうか。

let someString: String! = "this is the string"

より便利になるだろう:

let someString: String = "this is the string"

「オプションが定数または変数に「値なし」を許可することを示す」が、「オプションの値が最初に設定された後でオプションが常に値を持つことがプログラムの構造から明らかな場合」そもそもそれをオプションにする?あなたがオプションが常に値を持つことを知っているなら、それはそれがオプションではないということではないでしょうか?

476
Johnston

構築中および構成中にnilプロパティを持つ可能性があるが、その後は不変で非nilであるオブジェクトの場合を考えてみましょう(NSImageはこの方法で扱われることが多いですが、それでも場合によっては変更すると便利です)。暗黙のうちにラップされていないオプションは、比較的安全性の損失が少なく、コードをかなりクリーンアップします(1つの保証が守られている限り、安全です)。

(編集)しかし明確にするために:通常のオプションはほぼ常に望ましいです。

125
Catfish_Man

Implicitly Unwrapped Optionalsのユースケースを説明する前に、SwiftにOptionalsとImplicitly Unwrapped Optionalsが何を含んでいるのかを理解しておく必要があります。そうでなければ、私はあなたが最初に オプションに関する私の記事を読むことを勧めます

暗黙的にラップされていないオプションを使用する場合

Implicitly Unwrapped Optionalを作成する理由は2つあります。そうでなければ、Swiftコンパイラーは常にOptionalを明示的にラップ解除するように強制するので、すべてはnil時にアクセスされない変数を定義することと関係があります。

1.初期化中に定義できない定数

初期化が完了するまでに、すべてのメンバ定数に値が必要です。初期化中に定数を正しい値で初期化できないことがありますが、アクセスされる前に値があることが保証されている場合もあります。

Optionalは自動的にnilで初期化され、最終的に含まれる値は不変になるため、Optional変数を使用するとこの問題を回避できます。しかし、あなたが確実にnilではないことを知っている変数をアンラップすることは絶えず苦痛です。暗黙的にアンラップされたオプショナルは、オプショナルと同じ利点を達成しますが、どこにでも明示的にそれをアンラップする必要がないという追加の利点があります。

この良い例は、ビューがロードされるまでメンバー変数をUIViewサブクラスで初期化できない場合です。

class MyView: UIView {
    @IBOutlet var button: UIButton!
    var buttonOriginalWidth: CGFloat!

    override func awakeFromNib() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}

ここでは、ビューがロードされるまでボタンの元の幅を計算することはできませんが、awakeFromNibはビュー上の他のどのメソッドよりも前に呼び出されることがわかります(初期化以外)。クラス全体で無意味に明示的にラップ解除するように値を強制する代わりに、暗黙的にラップ解除したオプションとして値を宣言できます。

2.アプリがnilである変数から回復できない場合

これは非常にまれですが、アクセス時に変数がnilである場合にアプリを実行できない場合は、nilをテストする手間がかかります。通常、アプリの実行を継続するために絶対に当てはまる条件がある場合は、assertを使用します。暗黙的にラップされていないオプションには、nilのアサートが組み込まれています。それでも、オプションのラップを解除し、それがnilであれば、より説明的なアサーションを使用するのが良いことがよくあります。

暗黙的にラップされていないオプションを使用しない場合

1.遅延計算されたメンバ変数

決してnilにならないメンバー変数がある場合がありますが、初期化中に正しい値に設定できないことがあります。 1つの解決策は暗黙的にラップ解除されたオプションを使用することですが、もっと良い方法は遅延変数を使用することです。

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}

現在、メンバ変数contentsは、最初にアクセスされるまで初期化されません。これにより、クラスは初期値を計算する前に正しい状態に入ることができます。

注:これは上記の#1と矛盾するように思われるかもしれません。ただし、重要な区別があります。プロパティがアクセスされる前にだれもがボタンの幅を変更しないようにするために、viewDidLoad中に上記のbuttonOriginalWidthを設定する必要があります。

他のどこでも

誤って使用すると、nil中にアクセスしたときにアプリ全体がクラッシュするため、ほとんどの場合、暗黙的にラップされていないオプショナルを使用しないでください。変数がnilになる可能性があるかどうかわからない場合は、常にデフォルトの通常のOptionalを使用してください。決してnilではない変数をアンラップしても、それほど害はありません。

448
drewag

暗黙的にラップされていないオプションは、実際にカバーの下でオプションである必要があるときに、オプションを非オプションとして提示するのに役立ちます。これは、お互いに参照を必要とする2つの関連オブジェクトの間に「結び目を作る」ために必要です。どちらの参照も実際にオプションではない場合には意味がありますが、ペアが初期化されている間はそのうちの1つをnilにする必要があります。

例えば:

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"

どのBインスタンスも常に有効なmyBuddyA参照を持つ必要があるので、ユーザーにそれをオプションとして扱わせたくありませんが、参照するBを作成する前にAを作成できるようにするにはオプションにする必要があります。

しかしながら!この種の相互参照の要件は、密結合と貧弱な設計を示していることがよくあります。暗黙のうちにラップされていないオプショナルに頼っていると感じる場合は、おそらく相互依存を排除​​するためにリファクタリングを検討する必要があります。

55
n8gray

暗黙のうちにラップされていないオプションは、既存のCocoaフレームワークとその慣習との相互運用が必要なハイブリッド環境での作業をより快適にする一方で、Swiftコンパイラによって実行される安全なプログラミングパラダイムへの段階的な移行も可能にします。

Swift book、 基本 章、 暗黙のうちにラップされていないオプション

暗黙的にラップされていないオプションは、オプションが最初に定義された直後にオプションの値が存在することが確認され、その後のあらゆる時点で存在すると確実に仮定できる場合に役立ちます。 Swiftでの暗黙的にラップされていないオプションの主な用途は、 未所有の参照と暗黙的にラップされていないオプションのプロパティ で説明されているように、クラスの初期化中です。

暗黙のうちにラップされていないオプションは、使用されるたびに自動的にラップが解除されるように許可を与えるものと考えることができます。使用するたびにオプションの名前の後に感嘆符を付けるのではなく、宣言するときにオプションのタイプの後に感嘆符を付けます。

これは、 non-nilname _ _ness というプロパティが使用法によって設定され、クラスの初期化中にコンパイラによって強制できないというユースケースになります。たとえば、NIBまたはストーリーボードから初期化されるUIViewControllername__プロパティ。初期化は別々のフェーズに分割されますが、viewDidLoad()の後は通常、プロパティが存在すると見なすことができます。そうでなければ、コンパイラを満足させるために、 強制展開オプションバインディング または オプションチェーン を使用しなければなりませんでした。コードの主な目的を曖昧にするためだけのものです。

Swiftの本の上の部分は 自動参照カウント chapter も参照しています。

ただし、3つ目のシナリオがあります。両方のプロパティが常に値を持ち、初期化が完了した後はどちらのプロパティもnilname__にならないようにするというシナリオです。このシナリオでは、一方のクラスの所有されていないプロパティを、もう一方のクラスの暗黙的にラップされていないオプションのプロパティと組み合わせると便利です。

これにより、初期化が完了すると、参照サイクルを回避しながら、両方のプロパティに直接アクセスすることができます(オプションでアンラップすることなく)。

これは、ガベージコレクトされた言語ではないという癖に帰着するので、保持サイクルを破ることはプログラマーとしてのあなたにあり、暗黙のうちにラップされていないオプションはこの癖を隠すためのツールです。

これは、「暗黙的にラップされていないオプションをコード内で使用する場合」の質問をカバーしています。アプリケーション開発者として、あなたはObjective-Cで書かれたライブラリのメソッドシグネチャでそれらに遭遇することが多いでしょう。

からCocoaとObjective-CでSwiftを使う、セクション nilを使った作業

Objective-Cはオブジェクトがnil以外であることを保証しないため、SwiftはインポートされたObjective-C APIではすべてのクラスを引数型および戻り型をオプションにします。 Objective-Cのオブジェクトを使用する前に、それが存在しないことを確認する必要があります。

場合によっては、Objective-Cのメソッドまたはプロパティがnilname__オブジェクト参照を決して返さないことを 絶対的に 確実にすることができます。この特別なシナリオのオブジェクトをより使いやすくするために、Swiftはオブジェクトタイプを 暗黙的にラップされていないオプション としてインポートします。暗黙的にラップされていないオプションタイプには、オプションタイプの安全機能がすべて含まれています。さらに、nilname__を確認したり自分で展開したりすることなく、値に直接アクセスできます。最初に安全に展開しないでこの種のオプション型の値にアクセスすると、暗黙的に展開されたオプションは値が欠落しているかどうかを確認します。値が欠落していると、ランタイムエラーが発生します。結果として、値が欠落していることが確実でない限り、暗黙的にラップされていないオプションを常にチェックしてアンラップする必要があります。

...そしてここを越えて dragons

37
Palimondo

単一行(または複数行)の単純な例では、オプションの動作をうまく説明できません。変数を宣言してすぐに値を指定しても、オプションには意味がありません。

私がこれまで見た中で最高のケースは、オブジェクトの初期化の後に起こるセットアップであり、その後にそのセットアップに従うことが「保証されている」使用が続きます。 View Controllerの場合:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

printSizeはビューがロードされた後に呼び出されることを知っています - それはそのビューの中のコントロールにフックされたアクションメソッドであり、そうでなければ呼び出さないようにしました。そのため、!を使ってオプションのチェックやバインディングを省くことができます。 Swiftはその保証を認識できません(少なくともAppleが停止問題を解決するまで)ので、コンパイラにそれが存在することを伝えます。

しかし、これは型の安全性をある程度壊します。暗黙のうちにラップされていないオプションがあるところはどこでも、あなたの "保証"が必ずしも成り立たない場合にあなたのアプリがクラッシュする可能性がある場所ですので、それは控えめに使用する機能です。その上、!をいつも使うことはあなたが叫んでいるように聞こえます、そして誰もそれが好きではありません。

19
rickster

AppleはSwift Programming Language - >の自動参照カウント ですばらしい例を挙げています。 - >クラスインスタンス間の強い参照サイクルの解決 - >所有されていない参照と暗黙的にラップされていないオプションのプロパティ

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Cityのイニシャライザは、Countryのイニシャライザ内から呼び出されます。ただし、 2フェーズ初期化 で説明されているように、Countryのイニシャライザは、新しいselfインスタンスが完全に初期化されるまでCityCountryイニシャライザに渡すことはできません。

この要件に対処するには、capitalCityCountryプロパティを暗黙的にラップされていないオプションのプロパティとして宣言します。

15
fujianjin6471

確かに知っているのであれば、nilではなく、オプションからの戻り値、暗黙的にラップされていないオプションを使用して、オプションおよび非オプションから値を直接取得できます。 t。

//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString

だからこれはの使用の違いです

let someString : String!let someString : String

3
enadun

暗黙のオプションの理論的根拠は、最初に強制的な展開の理論的根拠を見ることによって説明するのが簡単です。

!を使用して、オプション(暗黙的かどうかに関係なく)のラップを強制的に解除します。これは、コードにバグがなく、オプションのコードにラップされていない部分の値がすでに含まれていることを意味します。なしで!演算子、あなたはおそらく単にオプションのバインディングでアサートするでしょう:

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

これほど素敵ではない

println("\(value!)")

さて、暗黙のオプションは、すべての可能なフローにおいて、alwaysがラップされていないときに値を持つことを期待するオプションを持つことを表現させます。それで、それはちょうどあなたを助けることにおいてさらに一歩前進する - 書くことの必要条件を緩和することによって!毎回アンラップし、フローに関する仮定が間違っている場合でもランタイムがエラーを起こすようにします。

3
Danra

Optionalは、多くの初心者を混乱させるこの構成の悪い名前だと思います。

他の言語(KotlinやC#など)ではNullableという用語を使用しているため、これを理解しやすくなります。

Nullableは、この型の変数にnull値を割り当てることができることを意味します。したがって、Nullable<SomeClassType>の場合、nullを割り当てることができます。SomeClassTypeだけの場合は、できません。それがSwiftの仕組みです。

なぜそれらを使用するのですか?まあ、時にはnullが必要になることがあります、それが理由です。たとえば、クラスにフィールドが必要であることはわかっているが、そのクラスのインスタンスを作成するときにフィールドを何かに割り当てることはできませんが、後で行うことができます。人々はすでにここでそれらを提供しているので、私は例を挙げません。私はこれを書いて2セントを差し上げています。

ところで、これがKotlinやC#などの他の言語でどのように機能するかを見てみることをお勧めします。

Kotlinのこの機能を説明するリンクは次のとおりです。 https://kotlinlang.org/docs/reference/null-safety.html

JavaやScalaなどの他の言語にはOptionalsがありますが、JavaとScalaのOptionalsとは動作が異なります。タイプはすべてデフォルトでnull可能です。

全体として、この機能はNullableではなく、SwiftでOptionalと命名されるべきだったと思います...

0
hey_you