someFunction(completion: { [weak self] in
self?.variable = self!.otherVariable
})
これは常に安全ですか?私はステートメントの冒頭でオプションのself
にアクセスします。個人的には、self
がnil
の場合、このステートメントの2番目の部分は実行されないと想定しています。これは本当ですか? self
が実際にnil
の場合、2番目の部分は決して発生しませんか?そして、この1行のコードの実行中にself
が 'nilled'になる可能性は決してありませんか?
オプションの連鎖 「Swiftプログラミング言語」からの例は、次の例を示しています。
let john = Person() // ... let someAddress = Address() // ... john.residence?.address = someAddress
続いて(強調を追加):
この例では、john.residenceが現在nilであるため、john.residenceのアドレスプロパティを設定しようとしても失敗します。
代入はオプションの連鎖の一部です。つまり、=演算子の右側のコードは評価されません。
あなたのケースに適用:
self?.variable = self!.otherVariable
self
がnil
の場合、右側はnotと評価されます。したがって、あなたの質問への答え
自己が本当にnilの場合、2番目の部分は決して発生しませんか?
「はい」です。 2番目の質問に関して
そして、この1行のコードの実行中に、自己が「埋もれる」ことは決してないでしょうか。
私の元の仮定は、self
が!= nil
であると判断されると、self!
への強い参照が、これは起こり得ないように、ステートメント。ただし(@Hamishが指摘したように)、これは保証されません。 AppleエンジニアJoe Groffが Confirming order of operations Swiftフォーラムで書いています:
これは保証されていません。リリースは、これよりも前に、最後に強参照が正式に使用された後の任意の時点で最適化される場合があります。左側[
weakProperty?.variable
]を評価するためにロードされた強参照は後で使用されないため、有効な状態を維持するものがないため、すぐに解放できます。weakProperty
によって参照されるオブジェクトの割り当てが解除される原因となる変数のゲッターに副作用がある場合、弱い参照を削除してから、これにより、右側の強制アンラップが失敗します。ifを使用して弱い参照をテストし、if let
によってバインドされた強い参照を参照する必要があります
以下のコメントで@Hamishによって指摘されているように、Swiftコンパイラエンジニア Joe Groffが説明 RHSの期間中、強い参照が保持される保証はありません。 '評価[強調マイン]
作業順序の確認
Rod_Brown:
こんにちは、
弱い変数へのアクセスのタイプの安全性について疑問に思っています:
_class MyClass { weak var weakProperty: MyWeakObject? func perform() { // Case 1 weakProperty?.variable = weakProperty!.otherVariable // Case 2 weakProperty?.performMethod(weakProperty!) } }
_上記の2つのケースで、Swiftによって、
weakProperty
をこれらの位置で強制的にラップ解除できることが保証されますか?保証について興味がありますSwiftは、オプションのチェーン中にアクセスについて行うたとえば、_
weakProperty!
_アクセサーは、オプションのチェーンが最初に値が既にnil
でないと判断した場合にのみ起動することが保証されています?さらに、この評価の期間中、弱いオブジェクトは保持されることが保証されますか、または弱い変数は、オプションのアクセスと呼び出されるメソッドの間で割り当てを解除できる可能性がありますか?
Joe_Groff:
これは保証されていません。リリースは、これよりも前に、最後に強参照が正式に使用された後の任意の時点で最適化される場合があります。 左側(
weakProperty?.variable
_)を評価するためにロードされた強参照は後で使用されないため、それを維持するものは何もないため、すぐに解放できます。変数のゲッターに副作用があり、weakProperty
によって参照されるオブジェクトの割り当てが解除される場合、nil
-弱い参照を除外すると、右側で強制的にアンラップすると失敗します。 if letを使用して弱参照をテストし、if letによってバインドされた強参照を参照する必要があります。_if let property = weakProperty { property.variable = property.otherVariable property.performMethod(property) }
_弱参照は4回ではなく1回読み込まれてテストされるため、これはより安全で効率的です。
上記のJoe Groffの回答を引用すると、私の以前の回答は意味がありませんが、Swiftコンパイラーの深部へのおそらく興味深い(失敗したとしても)旅としてここに残します。
この回答は、@ appzYourLife:s削除済みの回答に対するコメントに基づいて作成します。
これは純粋な推測ですが、経験豊富なSwiftコア開発者とC++:s Boost libの間のいくぶん緊密な関係を考えると、
weak
参照は有効期間中、強い参照にロックされていると思いますこれは、C++の対応する関数の明示的に使用されるstd::weak_ptr::lock()
と同様に、self
で何かを割り当て/変更する場合の式の.
self
がweak
参照によってキャプチャされ、割り当て式の左側にアクセスするときにnil
ではない例を見てみましょう。
_self?.variable = self!.otherVariable
/* ^ ^^^^^-- what about this then?
|
\-- we'll assume this is a success */
_
Swift runtime、 _Swift/include/Swift/Runtime/HeapObject.h
_具体的に でweak
(Swift)参照の基本的な扱いを見ることができます。
_/// Load a value from a weak reference. If the current value is a /// non-null object that has begun deallocation, returns null; /// otherwise, retains the object before returning. /// /// \param ref - never null /// \return can be null Swift_RUNTIME_EXPORT HeapObject *Swift_weakLoadStrong(WeakReference *ref);
_
ここで重要なのはコメントです
現在の値が割り当て解除を開始したnull以外のオブジェクトの場合、nullを返します。それ以外の場合、は戻る前にオブジェクトを保持します。
これはバックエンドランタイムコードのコメントに基づいているため、まだいくらか投機的ですが、上記のことは、weak
参照が指す値にアクセスしようとすると、実際に参照が存続期間中強力なものとして保持されることを意味します呼び出しの( "...戻るまで")。
上記の「やや投機的」の部分を引き換えようとするために、Swiftがweak
を介して値のアクセスを処理する方法を掘り下げますリファレンス @ idmean:sのコメントの下 (OP:sのような例の生成されたSILコードを調べる)から、Swift_weakLoadStrong(...)
関数が呼び出されていることがわかります。
したがって、まず _Swift/stdlib/public/runtime/HeapObject.cpp
_ のSwift_weakLoadStrong(...)
関数の実装を調べ、そこからどこに到達するかを確認します。
_HeapObject *Swift::Swift_weakLoadStrong(WeakReference *ref) { return ref->nativeLoadStrong(); }
_
_Swift/include/Swift/Runtime/HeapObject.h
_ からWeakReference
のnativeLoadStrong()
メソッドの実装を見つけます
_HeapObject *nativeLoadStrong() { auto bits = nativeValue.load(std::memory_order_relaxed); return nativeLoadStrongFromBits(bits); }
_
同じファイル から、nativeLoadStrongFromBits(...)
の実装:
_HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) { auto side = bits.getNativeOrNull(); return side ? side->tryRetain() : nullptr; }
_
コールチェーンに沿って続けると、tryRetain()
はHeapObjectSideTableEntry
のメソッドであり(これは オブジェクトライフサイクルステートマシン に不可欠です)、その実装は _Swift/stdlib/public/SwiftShims/RefCount.h
_
_HeapObject* tryRetain() { if (refCounts.tryIncrement()) return object.load(std::memory_order_relaxed); else return nullptr; }
_
RefCounts
タイプのtryIncrement()
メソッドの実装(ここでは typedef
:ed特殊化のインスタンスを介して呼び出されます )は次のように見つかります と同じファイル内上記 :
_// Increment the reference count, unless the object is deiniting. bool tryIncrement() { ... }
_
ここでのコメントは、このメソッドをエンドポイントとして使用するのに十分であると考えています。オブジェクトがdeinitingでない場合(上で想定しているように、そうではありません。OP:sの例のlhs
の代入は、成功)、オブジェクトの(強力な)参照カウントが増加し、HeapObject
ポインター(強い参照カウントの増分に基づく)が代入演算子に渡されます。対応する参照カウントの減分が最終的に割り当ての最後に実行される方法を調べる必要はありませんが、weak
参照に関連付けられたオブジェクトは、割り当ての存続期間中、強力なオブジェクトとして保持されることが推測を超えて、 @ MartinR:s answer で説明されているように、左側へのアクセス時に解放/割り当て解除されていません(この場合、右側は処理されません)。 )。
これは常に安全ですか
いいえ。あなたは「弱いダンス」をしていません。やれ! weak self
を使用する場合は常に、オプションを安全にアンラップし、そのアンラップの結果のみを参照する必要があります—次のように:
someFunction(completion: { [weak self] in
if let sself = self { // safe unwrap
// now refer only to `sself` here
sself.variable = sself.otherVariable
// ... and so on ...
}
})
ドキュメントは明らかに states であり、割り当ての左側がnilであると判断された場合、右側は評価されません。ただし、指定された例では、self
はweak referenceであり、オプションのチェックに合格した直後に解放(および無効化)できますが、 force-unwrapが発生する前に、式全体をnil-unsafeにします。