遅延初期化の次の解決策の両方が正しいかどうかを知りたいです。
一度だけ存在する必要がある他のクラスへの参照を保持することになっているクラスAppContext
があります(これらのクラスのすべてをシングルトンにすることを避けます)。これらの他のクラスの 1 つが と呼ばれるとしましょうReferencedClass
。そうは言っても、スレッドセーフな方法で、参照をデフォルトで遅延初期化したいと思います。
それは以前に議論されており、私はそれについてたくさん読んだことがありますが、私はまだ確信が持てません. 個人的な好みはさておき、私が知りたいのは、これら 2 つのソリューションは、私の望ましい動作を実装するための正しい方法ですか?
解決策 1:もともと、次のように実装したかったのです。
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for lock and check again after locking.
if (_referencedClass == nil) {
@synchronized(self) {
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
}
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
@synchronized(self) {
_referencedClass = referencedClass;
}
}
解決策 2:次に、代わりに GCD を使用することにしたので、次のように書きました。
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for "lock" and check again after "locking".
if (_referencedClass == nil) {
dispatch_sync(syncDispatchQueue, ^{
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
});
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
dispatch_sync(syncDispatchQueue, ^{
_referencedClass = referencedClass;
});
}
もちろん、どこか (たとえばinit
-Method 内)で次のsyncDispatchQueue
ようなものを初期化しました。
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
これは正しい、スレッドセーフでデッドロックのないコードですか? -変数と一緒に double-checked-locking を使用できますtemp
か? このダブルチェック ロックが安全でない場合、外側のチェックを削除すれば、両方のケースで私のコードは安全でしょうか? そうですかね。
よろしくお願いします!
[補足: 私は dispatch_once を認識しており、(Apple のドキュメントに反して) インスタンス変数でも使用できると言う人もいます。今のところ、これら 2 つのオプションのいずれかを使用したいと思います。もし可能なら。]