เหตุใด Apple แนะนำให้ใช้ dispatch_once สำหรับการนำรูปแบบซิงเกิลไปใช้ภายใต้ ARC


305

อะไรคือเหตุผลที่แน่นอนสำหรับการใช้ dispatch_once ใน accessor อินสแตนซ์ที่แชร์ของ singleton ภายใต้ ARC

+ (MyClass *)sharedInstance
{
    //  Static local predicate must be initialized to 0
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

มันเป็นความคิดที่ดีไหมหรือที่จะยกตัวอย่างซิงเกิลตันแบบอะซิงโครนัสในพื้นหลัง? ฉันหมายความว่าจะเกิดอะไรขึ้นถ้าฉันขออินสแตนซ์ที่ใช้ร่วมกันนั้นและใช้งานได้ทันที แต่ dispatch_once ใช้เวลาจนถึงคริสต์มาสเพื่อสร้างวัตถุของฉัน มันไม่กลับมาทันทีใช่มั้ย อย่างน้อยก็น่าจะเป็นจุดรวมของ Grand Central Dispatch

เหตุใดพวกเขาจึงทำเช่นนี้


Note: static and global variables default to zero.
ikkentim

คำตอบ:


418

dispatch_once()ซิงโครนัสอย่างแน่นอน ไม่ใช่วิธี GCD ทั้งหมดที่ทำแบบอะซิงโครนัส (ตัวพิมพ์เล็กและใหญ่dispatch_sync()) การใช้dispatch_once()แทนสำนวนต่อไปนี้:

+ (MyClass *)sharedInstance {
    static MyClass *sharedInstance;
    @synchronized(self) {
        if (sharedInstance == nil) {
            sharedInstance = [[MyClass alloc] init];
        }
    }
    return sharedInstance;
}

ประโยชน์ของการทำแบบdispatch_once()นี้คือมันเร็วกว่า นอกจากนี้ยังมีความหมายที่สะอาดกว่าเพราะช่วยปกป้องคุณจากหลายเธรดที่ทำจัดสรร init ของ SharedInstance ของคุณ - หากพวกเขาทั้งหมดลองในเวลาที่แน่นอน ไม่อนุญาตให้สร้างสองอินสแตนซ์ แนวคิดทั้งหมดของdispatch_once()"ทำบางสิ่งบางอย่างเพียงครั้งเดียวเท่านั้น" ซึ่งเป็นสิ่งที่เรากำลังทำอยู่


4
เพื่อเหตุผลของการโต้เถียงฉันต้องทราบว่าเอกสารไม่ได้บอกว่ามันถูกดำเนินการพร้อมกัน มีเพียงกล่าวว่าการเรียกใช้พร้อมกันหลายครั้งจะถูกทำให้เป็นอนุกรม
ผู้เชี่ยวชาญ

5
คุณอ้างว่ามันเร็วกว่า - เร็วแค่ไหน? ฉันไม่มีเหตุผลที่จะคิดว่าคุณไม่ได้พูดความจริง แต่ฉันต้องการดูมาตรฐานง่ายๆ
Joshua Gross

29
ฉันเพิ่งสร้างมาตรฐานง่ายๆ (บน iPhone 5) และดูเหมือนว่า dispatch_once นั้นเร็วกว่า @synchronized ประมาณ 2x
Joshua Gross

3
@ReneDohan: หากคุณมั่นใจ 100% ว่าไม่มีใครเคยเรียกวิธีการนี้จากเธรดอื่น แต่การใช้dispatch_once()นั้นง่ายมาก (โดยเฉพาะอย่างยิ่งเพราะ Xcode จะเติมข้อความอัตโนมัติให้เป็นข้อมูลโค้ดแบบเต็มสำหรับคุณ) และหมายความว่าคุณไม่จำเป็นต้องพิจารณาว่าวิธีการนั้นจะต้องปลอดภัยต่อเธรดหรือไม่
Lily Ballard

1
@ Siuying: อันที่จริงมันไม่จริง ก่อนอื่นสิ่งที่+initializeเกิดขึ้นก่อนที่ชั้นจะถูกแตะต้องแม้ว่าคุณยังไม่ได้พยายามสร้างอินสแตนซ์ที่แชร์ของคุณ โดยทั่วไปการเริ่มต้นขี้เกียจ (การสร้างบางสิ่งเมื่อจำเป็น) จะดีกว่า ประการที่สองแม้การเรียกร้องประสิทธิภาพของคุณไม่เป็นความจริง dispatch_once()ใช้เวลาเกือบตรงจำนวนเดียวกันของเวลาเป็นคำพูดในif (self == [MyClass class]) +initializeหากคุณมีอยู่+initializeแล้วใช่สร้างอินสแตนซ์ที่ใช้ร่วมกันจะเร็วขึ้น แต่คลาสส่วนใหญ่ไม่ได้
Lily Ballard

41

เพราะมันจะทำงานเพียงครั้งเดียว ดังนั้นหากคุณลองและเข้าถึงสองครั้งจากกระทู้ที่ต่างกันมันจะไม่ทำให้เกิดปัญหา

Mike Ash มีคำอธิบายแบบเต็มในการดูแลและให้อาหารของโพสต์บล็อกSingletonsของเขา

มีการเรียกใช้บล็อก GCD ทั้งหมดไม่ตรงกัน


Lily's เป็นคำตอบที่ดีกว่า แต่ฉันปล่อยให้ฉันเก็บลิงค์ไปยังโพสต์ของ Mike Ash
Abizern
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.