5

मैं इस दुर्घटना के पीछे कारणों को समझने की कोशिश कर रहा हूं कि ब्लॉकों का व्यवहार कैसे होता है। इस दुर्घटना को ट्रिगर करने के लिए मेरे पास वास्तव में एक साधारण वर्ग है।प्रेषण के साथ क्रैश

@implementation BlockCrashTest 

- (void)doSomething 
{ 
    dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL); 

    __weak typeof(self) weakSelf = self; 

    dispatch_block_t block = ^{ 

     __strong typeof(weakSelf) strongSelf = weakSelf; 

     dispatch_group_t group = dispatch_group_create(); 

     dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); 

     dispatch_group_enter(group); 
     [strongSelf performSomethingAsync:^{ 
      dispatch_group_leave(group); 
     }]; 

     if(dispatch_group_wait(group, time) != 0) { 
      NSLog(@"group already finished"); 
     } 
    }; 
    dispatch_async(queue, block); 
} 

- (void)performSomethingAsync:(void(^)(void))completion 
{ 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     sleep(5); 
     completion(); 
    }); 
} 

- (void)dealloc 
{ 
    NSLog(@"released object"); 
} 

@end 

अब, अगर मैं कक्षा का आवंटन और केवल विधि DoSomething इसे करने के लिए कहते हैं,

BlockCrashTest *someObject = [[BlockCrashTest alloc] init]; 
[someObject doSomething]; 

यह अपवाद के साथ दुर्घटनाओं, EXC_BAD_INSTRUCTION और निम्न स्टैक ट्रेस,

#0 0x000000011201119a in _dispatch_semaphore_dispose() 
#1 0x0000000112013076 in _dispatch_dispose() 
#2 0x0000000112026172 in -[OS_dispatch_object _xref_dispose]() 
#3 0x000000010ef4c2fd in __29-[BlockCrashTest doSomething]_block_invoke at /Users/Sandeep/Desktop/Test Block Crash/Test Block Crash/ViewController.m:35 
#4 0x0000000112005ef9 in _dispatch_call_block_and_release() 

यदि मैं विधि को संशोधित करता हूं तो कुछ,, ऐसी है कि वह कमजोर का उपयोग नहीं करते लेकिन का उपयोग करता है स्वयं तो दुर्घटना नहीं होती है और तरीकों के रूप में उम्मीद निष्पादित करने के लिए लग रहे हैं

- (void)doSomething 
{ 
    dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL); 

    dispatch_block_t block = ^{ 

     dispatch_group_t group = dispatch_group_create(); 

     dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); 

     dispatch_group_enter(group); 
     [self performSomethingAsync:^{ 
      dispatch_group_leave(group); 

     }]; 

     if(dispatch_group_wait(group, time) != 0) { 
      NSLog(@"group already finished"); 
     } 
    }; 
    dispatch_async(queue, block); 
} 

यह क्यों दुर्घटना करता है, मैं समझता हूँ कि ब्लॉक के अंदर कमजोर उपयोग कर रहा है यह सुनिश्चित करेगा कि ऑब्जेक्ट जारी होने पर विधि को नहीं कहा जाएगा और मैंने सोचा था कि कमजोर ब्लॉक के अंदर स्वयं का उपयोग करने से सुरक्षित है।

ऊपर कोड weakSelf साथ ठीक काम करता है, अगर मैं वस्तु BlockCrashTest को बनाए रखने और इसे करने के लिए विधि कॉल।

अगर कोई दुर्घटना के पीछे कारण बता सकता है और कोड के इन 3 अलग-अलग प्रकारों के साथ वास्तव में क्या होता है तो मैं वास्तव में खुश हूं कि एक दुर्घटना और अन्य ठीक काम करने लगते हैं।

नोट: यह एक ही रास्ता या धागा, Objective-C crash on __destroy_helper_block_ में सूचीबद्ध दुर्घटना के अन्य संबंधित है। मैं ऊपर दिए गए कोड के साथ सटीक उसी स्टैक निशान को पुन: पेश करने में सक्षम हूं।

उत्तर

3

टिप्पणियों के एक जोड़े:

  1. आप के साथ एक प्रेषण समूह नहीं हो सकता असंतुलित "enter" और जब dispatch_group_t वस्तु पुनः आवंटित की जाती है "छोड़"। और जैसा कि ilya ने बताया, आपके पैटर्न की वजह से, strongSelfnil है, इसलिए आप समूह में प्रवेश कर रहे हैं, लेकिन इसे छोड़ नहीं रहे हैं।

    एक weakSelf और strongSelf नृत्य में बहुत आम पैटर्न सिर्फ देखने के लिए कि strongSelfnil था या नहीं की जांच करने के असंतुलन को दूर करने है। इस प्रकार, यदि strongSelfnil है, यह प्रेषण समूह सामान पूरी तरह नजरअंदाज, लेकिन अगर यह nil नहीं है, दोनों "दर्ज" और "छोड़" कहा जाएगा:

    - (void)doSomething { 
        dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL); 
    
        typeof(self) weakSelf = self; 
    
        dispatch_async(queue, ^{ 
         typeof(self) strongSelf = weakSelf; 
    
         if (strongSelf) { 
          dispatch_group_t group = dispatch_group_create(); 
    
          dispatch_group_enter(group); 
          [strongSelf performSomethingAsync:^{ 
           dispatch_group_leave(group); 
          }]; 
    
          dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
         } 
        }); 
    } 
    

    जाहिर है, आप यह सुनिश्चित करें कि बनाना चाहिए performSomethingAsync विधि , स्वयं, हमेशा उस समूह को कॉल करता है जो समूह को छोड़ देता है।

  2. इस के हल के लिए अन्य तरीके से (यदि आप आश्वासन की जरूरत नहीं है कि की "दर्ज" और "छोड़" संतुलित हो जाएगा सब), संकेतबाहु उपयोग करने के लिए है: सच कहूं

    - (void)doSomething { 
        dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL); 
    
        typeof(self) weakSelf = self; 
    
        dispatch_async(queue, ^{ 
         typeof(self) strongSelf = weakSelf; 
    
         dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
    
         [strongSelf performSomethingAsync:^{ 
          dispatch_semaphore_signal(semaphore); 
         }]; 
    
         dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); 
    
         if (dispatch_semaphore_wait(semaphore, time) != 0) { 
          NSLog(@"semaphore not received in time"); 
         } 
        }); 
    } 
    

    , यहां तक ​​कि सेमेफोर का उपयोग करते समय, जैसा कि मैंने ऊपर किया है, मुझे अभी भी लगता है कि कोई यह पुष्टि करना चाहता है कि strongSelfnil नहीं था। समवर्ती प्रोग्रामिंग nil ऑब्जेक्ट को संदेश की संभावना को जोड़ने के बिना पर्याप्त भ्रमित कर रहा है जिसके परिणामस्वरूप नो-ऑप होता है।

    - (void)doSomething { 
        dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL); 
    
        typeof(self) weakSelf = self; 
    
        dispatch_async(queue, ^{ 
         typeof(self) strongSelf = weakSelf; 
    
         if (strongSelf) { 
          dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
    
          [strongSelf performSomethingAsync:^{ 
           dispatch_semaphore_signal(semaphore); 
          }]; 
    
          dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); 
    
          if (dispatch_semaphore_wait(semaphore, time) != 0) { 
           NSLog(@"semaphore not received in time"); 
          } 
         } 
        }); 
    } 
    
+0

क्या आप मुझे ब्लॉक के अंदर स्वयं का उपयोग करने पर भी एक संकेत दे सकते हैं कि यह क्रैश क्यों नहीं होता है? क्या यह केवल इसलिए है कि ब्लॉक स्वयं को बरकरार रखता है? – Sandeep

+1

@ जनरेटरऑफोन - क्योंकि 'स्वयं' की उपस्थिति ब्लॉक के अंदर मजबूत संदर्भ स्थापित करती है, जिसे तब तक हल नहीं किया जाता है जब तक कि ब्लॉक निष्पादित नहीं हो जाता है। इस प्रकार 'कमजोर' 'शून्य 'नहीं है और' मजबूत स्वयं '' शून्य' नहीं है। इस प्रकार, 'execSomethingAsync' में आपका पूरा होने वाले हैंडलर ब्लॉक को कॉल किया जा रहा है, यह सुनिश्चित करना कि "एंटर" अब "छोड़ें" द्वारा संतुलित है। लेकिन यदि आप चाहते थे कि ब्लॉक वस्तु को बरकरार रखे, तो आप स्पष्ट रूप से कमजोर/मजबूत स्वयं नृत्य के माध्यम से नहीं गए होंगे। – Rob

+0

यह अच्छी व्याख्या है। इसलिए, मेरे पास कुछ ऑटोरेलेज्ड ऑब्जेक्ट हो सकता है जो अभी भी कुछ लंबी एसिंक्रोनस कॉल कर सकता है और यदि मैं कमजोर/मजबूत उपयोग करने के बजाय स्वयं का उपयोग करता हूं तो भी आसपास हो सकता है। यद्यपि यदि वस्तु स्वयं को बरकरार रखती है, तो यह कमजोर/मजबूत होने का अर्थ है, क्या यह इस बात का सारांश है? – Sandeep

2

यदि आप self performSomethingAsync: पर कॉल हटाते हैं तो भी आपको वही दुर्घटना मिल जाएगी। यह क्रैश libdispatch सेमफोर एपीआई के कारण होता है। आप Xcode में दुर्घटनाग्रस्त हो गया समारोह _dispatch_semaphore_dispose की विधानसभा का पता लगाने देख सकते हैं: enter image description here

हम यह पता लगाने की क्या इस कोड में होता है प्रयास करते हैं तो हम देखेंगे कि आप स्पष्ट रूप से निशान वर्तमान ब्लॉक dispatch_group_enter फोन करके समूह में प्रवेश किया । फिर performSomethingAsync कॉल नहीं करता है क्योंकि strongSelf == nil। जिसका अर्थ है कि dispatch_group_enterdispatch_group_leave के साथ संतुलित नहीं है इस कारण समूह ठीक से निपटान नहीं कर सका और दुर्घटनाग्रस्त हो गया (एएसएम लिस्टिंग देखें)।

आप self का उपयोग करते हैं इस कोड को भी दुर्घटनाग्रस्त हो गया क्योंकि dispatch_group_leave(group);dispatch_group_enter के साथ अलग अलग धागे से कहा जाता है जो भी एक ही दुर्घटना लेकिन एक और परिप्रेक्ष्य में पैदा कर रहा है: एक ही धागे में संतुलित नहीं कॉल। performSomethingAsync ने अलग कतार में समापन ब्लॉक कहा है "com.queue.test" में नहीं।

यह उदाहरण dispatch_groups एपीआई का उपयोग कर गलत है।यह देखने के लिए कि इसका उपयोग कैसे करें apple doc देखें।

1

MacOS 10.8 और iOS 6.0 वस्तुओं प्रेषण करने के लिए एआरसी की शुरुआत की। प्रलेखन GCD Objects and Automatic Reference Counting से:

जब आप ऑब्जेक्टिव-सी संकलक का उपयोग कर अपने ऐप्लिकेशन का निर्माण, सभी प्रेषण वस्तुओं ऑब्जेक्टिव-सी वस्तुएं हैं। इस प्रकार, जब स्वचालित संदर्भ गिनती (एआरसी) सक्षम होती है, तो प्रेषण वस्तुओं को किसी भी अन्य उद्देश्य-सी ऑब्जेक्ट की तरह स्वचालित रूप से बनाए रखा जाता है और जारी किया जाता है। जब एआरसी सक्षम नहीं होता है, तो अपनी प्रेषण वस्तुओं को बनाए रखने और रिलीज़ करने के लिए dispatch_retain और dispatch_release फ़ंक्शंस (या उद्देश्य-सी semantics) का उपयोग करें। आप कोर फाउंडेशन को बनाए रखने/रिलीज कार्यों का उपयोग नहीं कर सकते हैं।

यदि आपको बाद में तैनाती लक्ष्य (मौजूदा कोड के साथ संगतता बनाए रखने के लिए) के साथ एआरसी-सक्षम ऐप में सेमेन्टिक्स को बनाए रखने/रिलीज़ करने की आवश्यकता है, तो आप -DOS_OBJECT_USE_OBJC = 0 जोड़कर उद्देश्य-सी-आधारित प्रेषण ऑब्जेक्ट को अक्षम कर सकते हैं। आपके कंपाइलर झंडे।

आपके मामले में एआरसी खुशी से आपके dispatch_group_t के जीवन चक्र का प्रबंधन कर रहा है। और, दुर्भाग्यवश, आपका कोड समूह को रिलीज़ कर रहा है जबकि लॉक अभी भी प्रतीक्षा कर रहा है। जब समूह के समय को जारी किया जाता है - तो जब dispatch_group_leave इसे क्रैश कहा जाता है, क्योंकि समूह को पहले से ही जारी कर दिया गया है।

मैं कम से कम जांचने वाले गीलेर पर सुझाव दूंगा कि समूह इसे छोड़ने से पहले न्यूल है।

इसके अतिरिक्त, आपके प्रतीक्षा परिणाम तर्क को उलट दिया गया है। एक शून्य परिणाम इंगित करता है कि समूह टाइमआउट से पहले खाली हो गया था, एक गैर शून्य परिणाम इंगित करता है कि टाइमआउट मारा गया था।

संबंधित मुद्दे