سؤال

لقد جئت إلى أن هذا على رماد مايك "رعاية وإرضاء المفردات" وكان قليلا محبوبا من تعليقه:

هذا الرمز هو نوع من البطيء، رغم ذلك. أخذ قفل مكلف إلى حد ما. جعلها أكثر إيلاما هي حقيقة أن الغالبية العظمى من الوقت، والقفل بلا معنى. مطلوب القفل فقط عندما يكون foo لا شيء، والذي يحدث أساسا مرة واحدة فقط. بعد تهيئة Singleton، أصبحت الحاجة إلى القفل، لكن القفل نفسه يبقى.

+(id)sharedFoo {
    static Foo *foo = nil;
    @synchronized([Foo class]) {
        if(!foo) foo = [[self alloc] init];
    }
    return foo;
}

سؤالي هو، وليس هناك شك في سبب وجيه لذلك ولكن لماذا لا يمكنك الكتابة (انظر أدناه) للحد من القفل عند فو لا شيء؟

+(id)sharedFoo {
    static Foo *foo = nil;
    if(!foo) {
        @synchronized([Foo class]) {
            foo = [[self alloc] init];
        }
    }
    return foo;
}

هتاف غاري

هل كانت مفيدة؟

المحلول

لأنه ثم يخضع الاختبار لحالة سباق. اثنين من المواضيع المختلفة قد اختبار بشكل مستقل ذلك foo هو nil, ، ثم (بالتتابع) إنشاء مثيلات منفصلة. يمكن أن يحدث هذا في الإصدار المعدل عند قيام مؤشر ترابط واحد يؤدي الاختبار بينما لا يزال الآخر في الداخل +[Foo alloc] أو -[Foo init], ، ولكن لم تحدد بعد foo.

بالمناسبة، لن أفعل ذلك بهذه الطريقة على الإطلاق. تفحص ال dispatch_once() وظيفة، والتي تتيح لك الضمان أن يتم تنفيذ كتلة فقط مرة واحدة فقط أثناء عمر التطبيق (على افتراض أن لديك GCD على النظام الأساسي الذي تستهدفه).

نصائح أخرى

وهذا ما يسمى فحص مزدوج قفل "الأمثل". وبعد كما هو موثق في كل مكان هذا ليس آمنا. حتى إذا لم يهزمها تحسين مترجم، فسوف يتم هزيمة الطريقة التي تعمل بها الذاكرة على الأجهزة الحديثة، إلا إذا كنت تستخدم نوعا من السياج / الحواجز.

مايك الرماد يظهر أيضا الحل الصحيح باستخدام volatile و OSMemoryBarrier();.

المشكلة هي أنه عندما ينفذ مؤشر ترابط واحد foo = [[self alloc] init]; لا يوجد أي ضمان عندما يرى موضوع آخر foo != 0 جميع الكتب الذاكرة التي يؤديها init مرئي أيضا.

انظر أيضا DCL و C ++ و DCL و Java. لمزيد من التفاصيل.

في الإصدار الخاص بك الشيك !foo يمكن أن تحدث على خيوط متعددة في نفس الوقت، مما يتيح اثنين من الخيوط للقفز في alloc كتلة، واحدة في انتظار الآخر لإنهاء قبل تخصيص مثيل آخر.

يمكنك التحسين عن طريق أخذ القفل فقط إذا كان foo == nil، ولكن بعد ذلك تحتاج إلى اختبار مرة أخرى (ضمنsynchronized) للحماية من ظروف السباق.

+ (id)sharedFoo {
    static Foo *foo = nil;
    if(!foo) {
        @synchronized([Foo class]) {
            if (!foo)  // test again, in case 2 threads doing this at once
                foo = [[self alloc] init];
        }
    }
    return foo;
}

أفضل طريقة إذا كان لديك إيفاد الكبير cenral

+ (MySingleton*) instance {
 static dispatch_once_t _singletonPredicate;
 static MySingleton *_singleton = nil;

 dispatch_once(&_singletonPredicate, ^{
    _singleton = [[super allocWithZone:nil] init];
 });

 return _singleton
 }
+ (id) allocWithZone:(NSZone *)zone {
  return [self instance];
 }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top