2010-08-13 11 views
13

मैंने NSManagedObjectContext और बहु-थ्रेडेड अनुप्रयोगों के बारे में यहां कई पोस्ट पढ़ी हैं। मैं CoreDataBooks उदाहरण पर भी गया हूं यह समझने के लिए कि अलग-अलग धागे को अपने स्वयं के एनएसएमएनेज्ड ऑब्जेक्ट कॉन्टेक्स्ट की आवश्यकता होती है, और मुख्य एनएसएएनएजेडऑब्जेक्ट कॉन्टेक्स्ट के साथ एक सेव ऑपरेशन कैसे विलय हो जाता है। मुझे अच्छा होने का उदाहरण मिला, लेकिन यह भी आवेदन विशिष्ट है। मैं इसे सामान्य बनाने की कोशिश कर रहा हूं, और आश्चर्यचकित हूं कि मेरा दृष्टिकोण अच्छा है या नहीं।बहु-थ्रेडेड अनुप्रयोग में NSManagedObjectContext के जेनेरिक दृष्टिकोण

मेरा दृष्टिकोण वर्तमान धागे के लिए NSManagedObjectContext लाने के लिए एक सामान्य कार्य होना है। फ़ंक्शन मुख्य थ्रेड के लिए NSManagedObjectContext देता है, लेकिन एक अलग थ्रेड के भीतर से बुलाए जाने पर एक नया (या इसे कैश से लाया जाएगा) बना देगा। यह निम्नानुसार है:

+(NSManagedObjectContext *)managedObjectContext { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *moc = delegate.managedObjectContext; 

    NSThread *thread = [NSThread currentThread]; 

    if ([thread isMainThread]) { 
     return moc; 
    } 

    // a key to cache the context for the given thread 
    NSString *threadKey = [NSString stringWithFormat:@"%p", thread]; 

    // delegate.managedObjectContexts is a mutable dictionary in the app delegate 
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts; 

    if ([managedObjectContexts objectForKey:threadKey] == nil) { 
     // create a context for this thread 
     NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease]; 
     [threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]]; 
     // cache the context for this thread 
     [managedObjectContexts setObject:threadContext forKey:threadKey]; 
    } 

    return [managedObjectContexts objectForKey:threadKey]; 
} 

मुख्य धागे से बुलाए जाने पर सहेजें ऑपरेशन सरल होते हैं। अन्य धागे से बुलाए गए ऑपरेशंस को मुख्य थ्रेड के भीतर विलय की आवश्यकता होती है।

+(void)commit { 
    // get the moc for this thread 
    NSManagedObjectContext *moc = [self managedObjectContext]; 

    NSThread *thread = [NSThread currentThread]; 

    if ([thread isMainThread] == NO) { 
     // only observe notifications other than the main thread 
     [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(contextDidSave:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:moc]; 
    } 

    NSError *error; 
    if (![moc save:&error]) { 
     // fail 
    } 

    if ([thread isMainThread] == NO) { 
     [[NSNotificationCenter defaultCenter] removeObserver:self 
                name:NSManagedObjectContextDidSaveNotification 
                object:moc]; 
    } 
} 

contextDidSave: समारोह हम मर्ज, अगर commit में अधिसूचना द्वारा कहा जाता है: कि के लिए मैं एक सामान्य commit समारोह है।

+(void)contextDidSave:(NSNotification*)saveNotification { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *moc = delegate.managedObjectContext; 

    [moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
         withObject:saveNotification 
        waitUntilDone:YES]; 
} 

अंत में, हम साफ-अप NSManagedObjectContext का कैश इस के साथ:

+(void)initialize { 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(threadExit) 
               name:NSThreadWillExitNotification 
               object:nil]; 
} 

+(void)threadExit { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]]; 
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;  

    [managedObjectContexts removeObjectForKey:threadKey]; 
} 

यह संकलित करता है तथा काम करने के लिए लगता है, लेकिन मैं समस्याओं सूत्रण दौड़ की स्थिति के कारण मुश्किल हो सकता है पता है। क्या किसी को इस दृष्टिकोण के साथ कोई समस्या दिखाई देती है?

इसके अलावा, मैं इसे एसिंक्रोनस अनुरोध (ASIHTTPRequest का उपयोग करके) के संदर्भ में उपयोग कर रहा हूं, जो सर्वर से कुछ डेटा प्राप्त करता है और अपडेट करता है और आईफोन पर स्टोर को सम्मिलित करता है। ऐसा लगता है कि अनुरोध पूरा होने के बाद NSThreadWillExitNotification आग नहीं आता है, और उसके बाद उसी अनुरोध को बाद के अनुरोधों के लिए उपयोग किया जाता है। इसका मतलब है कि एक ही थ्रेड पर अलग-अलग अनुरोधों के लिए एक ही NSManagedObjectContext का उपयोग किया जाता है। क्या ये एक दिक्कत है?

+0

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

उत्तर

8

इस प्रश्न को पोस्ट करने के एक साल बाद मैंने अंततः कोर डेटा के साथ अपने काम को सामान्य बनाने और सरल बनाने के लिए एक ढांचा बनाया। यह मूल प्रश्न से परे है, और कोर डेटा इंटरैक्शन को अधिक आसान बनाने के लिए कई सुविधाएं जोड़ता है।विवरण यहां: https://github.com/chriscdn/RHManagedObject

0

मुझे समस्या को बेहतर ढंग से समझने के बाद समाधान मिला। मेरा समाधान उपरोक्त प्रश्न को सीधे संबोधित नहीं करता है, लेकिन समस्या को हल करता है कि मुझे पहले स्थान पर धागे से निपटना क्यों था।

मेरा एप्लिकेशन एसिंक्रोनस अनुरोधों के लिए ASIHTTPRequest लाइब्रेरी का उपयोग करता है। मैं सर्वर से कुछ डेटा प्राप्त करता हूं, और मेरे कोर-डेटा ऑब्जेक्ट्स को जोड़ने/संशोधित/हटाने/प्रतिनिधि requestFinished फ़ंक्शन का उपयोग करता हूं। requestFinished फ़ंक्शन एक अलग थ्रेड में चल रहा था, और मुझे लगता है कि यह असीमित अनुरोधों का एक प्राकृतिक दुष्प्रभाव था।

गहन जानकारी के बाद मैंने पाया कि ASIHTTPRequest जानबूझ कर एक अलग थ्रेड में अनुरोध चलाता है, लेकिन ASIHTTPRequest की मेरी उपवर्ग में अधिरोहित जा सकता है:

+(NSThread *)threadForRequest:(ASIHTTPRequest *)request { 
    return [NSThread mainThread]; 
} 

यह छोटा सा परिवर्तन मुख्य थ्रेड में requestFinished डालता है, जो समाप्त हो गया है मेरे आवेदन में धागे की देखभाल करने की मेरी ज़रूरत है।

+0

के लिए लॉक का उपयोग करने पर टिप्पणी करें, मुझे पूरा यकीन नहीं है कि मैं समझ गया हूं। ASIHTTPRequest एसिंक्रोनस अनुरोधों के लिए एक अलग थ्रेड (वास्तव में एक NSOperationQueue) का उपयोग करता है, लेकिन समान रूप से यह हमेशा अनुरोध चलाता है मुख्यधारा पर समाप्त होता है। (नवीनतम कोड में, यह कार्यक्षमता कॉल सिलेक्टरऑनमेन थ्रेड विधि में है।) उसने कहा कि मुझे आपके समाधान के लिए कोई नुकसान नहीं दिख रहा है। – JosephH

+0

मैंने पाया कि अनुरोध मुख्य धागे पर तब तक नहीं चला जब तक मैंने उपरोक्त उन तीन पंक्तियों को जोड़ा नहीं। क्या यह संभव है कि यह ASIHTTPRequest के साथ बदल गया है? मैं v1.7 का उपयोग कर रहा हूँ। – chris

+0

आप सही हैं। ASIHTTPRequest प्रतिनिधि मुख्य धागे पर चलाया जाता है, लेकिन मैं ASIHTTPRequest के उप-वर्ग को कार्यान्वित कर रहा था और अपना कोड 'requestFinished: 'विधि में डाल रहा था। यह मुख्य धागे पर जरूरी नहीं है। धन्यवाद – chris

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