2

遅延初期化の次の解決策の両方が正しいかどうかを知りたいです。

一度だけ存在する必要がある他のクラスへの参照を保持することになっているクラス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 つのオプションのいずれかを使用したいと思います。もし可能なら。]

4

1 に答える 1

4

私が理解している限り、割り当てはアトミックではないため、「ダブルチェックロック」メカニズムはスレッドセーフではありませ_referencedClass = ...ん。そのため、1 つのスレッドが外側のif (_referencedClass == nil)チェックで部分的に初期化された変数を読み取る可能性があります。

外側のチェックを外すと、どちらのバージョンも問題ないように見えます。

あなたは興味があるかもしれません

実装とパフォーマンスの違いを説明する素晴らしい答えがあります。

于 2013-09-17T08:47:50.123 に答える