核心数据locking在后台线程中

我在核心数据锁。 我真的不明白原因。 因为我在后台线程中处理时正在创build后台MOC。 下面你可以看到什么是堆栈跟踪(我暂停应用程序的执行)看起来像这样的时候:

Thread 1, Queue : com.apple.main-thread #0 0x32d2a0fc in __psynch_mutexwait () #1 0x3608b128 in pthread_mutex_lock () #2 0x365d2dac in -[_PFLock lock] () #3 0x365e3264 in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] () #4 0x365e1e2a in -[NSManagedObjectContext executeFetchRequest:error:] () #5 0x3664a93e in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] () #6 0x3664b0c8 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0 () #7 0x3932bd28 in _dispatch_barrier_sync_f_slow_invoke () Thread 10, Queue : EventKitHelperSyncSerialBackgroundQueue #0 0x32d19f04 in semaphore_wait_trap () #1 0x3932c300 in _dispatch_thread_semaphore_wait$VARIANT$mp () #2 0x3932a880 in _dispatch_barrier_sync_f_slow () #3 0x3663b9e6 in _perform () #4 0x3664adba in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] () #5 0x365e1e2a in -[NSManagedObjectContext executeFetchRequest:error:] () #6 0x000b11e4 in -[CoreDataHelper fetchEntity:predicate:andSortDescriptors:inManagedObjectContext:] at /Users/peterwarbo/Desktop/app/CoreDataHelper.m:110 #7 0x000ad648 in -[EventKitHelper processChangedCalendar] at /Users/peterwarbo/Desktop/app/EventKitHelper.m:242 #8 0x000ad3b4 in __54-[EventKitHelper syncInBackgroundWithCompletionBlock:]_block_invoke_0 at /Users/peterwarbo/Desktop/app/EventKitHelper.m:218 #9 0x3932711e in _dispatch_call_block_and_release () #10 0x3932aece in _dispatch_queue_drain$VARIANT$mp () #11 0x3932adc0 in _dispatch_queue_invoke$VARIANT$mp () #12 0x3932b91c in _dispatch_root_queue_drain () #13 0x3932bac0 in _dispatch_worker_thread2 () #14 0x36090a10 in _pthread_wqthread () #15 0x360908a4 in start_wqthread () 

EventKitHelperSyncSerialBackgroundQueue我正在做一些核心数据处理在后台队列。 ReminderNSManagedObject 。 对不起,代码的数量,但我认为最好不要留下任何重要的细节。

EventKitHelper.m

 - (void)syncInBackgroundWithCompletionBlock:(CalendarSyncCompletionBlock)block { DLogName() self.completionBlock = block; if (self.syncSerialBackgroundQueue == NULL) { self.syncSerialBackgroundQueue = dispatch_queue_create("EventKitHelperSyncSerialBackgroundQueue", 0); } dispatch_async(self.syncSerialBackgroundQueue, ^{ [self processChangedCalendar]; }); } - (void)processChangedCalendar { DLogName() CoreDataHelper *cdHelper = [CoreDataHelper sharedInstance]; // Store has been changed, events could be updated/deleted/added // Need to check if any of the user created Reminders are referencing the calendar // If so, update the affected Reminders // Predicate to fetch only Reminders that are of type (RMReminderDateServiceCalendarEvent or RMReminderDateServiceCalendarBirthday) AND status is not completed NSPredicate *userRemindersPredicate = [NSPredicate predicateWithFormat:@"(dateService == %@ OR dateService == %@) AND status != %@", @(RMReminderDateServiceCalendarEvent), @(RMReminderDateServiceCalendarBirthday), @(RMReminderStatusCompleted)]; // Sort the user's Reminders with the earliest date first NSSortDescriptor *dateSortAsc = [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:YES]; // Creating a new MOC for thread safety NSManagedObjectContext *syncContext = [cdHelper threadedManagedObjectContext]; self.syncContext = syncContext; NSArray *usersReminders = [[CoreDataHelper sharedInstance] fetchEntity:APReminderEntity predicate:userRemindersPredicate andSortDescriptors:@[dateSortAsc] inManagedObjectContext:syncContext]; if (usersReminders.count == 0) { DLog(@"User doesn't have any Calendar Reminders, no need to sync") BOOL error = NO; self.completionBlock(error); return; } else { if (!self.isCalendarAccessAuthorized) { DLog(@"Calendar access is not authorized and we have Calendar Reminders, alert the user") BOOL error = YES; self.completionBlock(error); return; } else { DLog(@"Calendar access is authorized") } } if (!self.calendarchanged) { DLog(@"Calendar not updated, no need to sync") BOOL error = NO; self.completionBlock(error); return; } DLog(@"Calendar updated, syncing...") NSDate *earliestReminderDate = [(Reminder *) [usersReminders objectAtIndex:0] date]; // Since there exists a possibility that a Calendar event can change date back in time, we should fetch events from our earliest Reminder date + 1 year back NSDate *eventsFromThisDate = [Utilities oneYearAgoForDate:[Utilities midnightDateForDate:earliestReminderDate]]; NSDate *endDate = [NSDate distantFuture]; // This will get me events 4 years from now // Create the predicate NSPredicate *eventStorePredicate = [self.eventStore predicateForEventsWithStartDate:eventsFromThisDate endDate:endDate calendars:nil]; // Fetch all events that match the predicate. NSArray *eventKitEvents = [self.eventStore eventsMatchingPredicate:eventStorePredicate]; NSMutableArray *events = [NSMutableArray arrayWithCapacity:100]; for (EKEvent *event in eventKitEvents) { NSString *eventTitle = [event title]; NSDate *eventDate = [event startDate]; NSDate *eventDateModified = [event lastModifiedDate]; NSString *eventID = [event eventIdentifier]; // Check if event is a Birthday event BOOL isBirthday = [event birthdayPersonID] != -1 ? YES : NO; RMReminderDateService dateService; if (isBirthday) { dateService = RMReminderDateServiceCalendarBirthday; } else { dateService = RMReminderDateServiceCalendarEvent; } RMDateEvent *calendarEvent = [[RMDateEvent alloc] initWithDate:eventDate dateModified:eventDateModified name:eventTitle dateService:dateService andID:eventID]; BOOL eventAlreadyAdded = NO; if (!eventAlreadyAdded) { [events addObject:calendarEvent]; } } for (Reminder *reminder in usersReminders) { NSPredicate *predicateID = [NSPredicate predicateWithFormat:@"ID == %@", reminder.dateServiceID]; NSArray *eventsMatchingID = [events filteredArrayUsingPredicate:predicateID]; RMDateEvent *event = [eventsMatchingID lastObject]; if (event == nil) { // We couldn't find the event by ID, try to find it by date AND title NSPredicate *predicateDateAndTitle = [NSPredicate predicateWithFormat:@"date == %@ AND name == %@", reminder.date, reminder.dateText]; NSArray *eventsMatchingDateAndTitle = [events filteredArrayUsingPredicate:predicateDateAndTitle]; event = [eventsMatchingDateAndTitle lastObject]; if (event == nil) { // We couldn't find the event, most likely it has been deleted from the user's events or the user has changed all values for our saved event :-( } else { // We found it by date AND title [self processReminder:reminder forDateEvent:event]; } } else { // We found it by ID [self processReminder:reminder forDateEvent:event]; } } [self fetchEventsFromNow]; [self processEventKitEvents]; #warning TODO: Broadcast a message to update the Reminder date AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; [appDelegate setTabCountInBackground]; self.calendarchanged = NO; DLog(@"Calendar sync done") BOOL error = NO; self.completionBlock(error); } - (void)processReminder:(Reminder *)reminder forDateEvent:(RMDateEvent *)event { NSDate *eventModifiedDate = [event dateModified]; if ([eventModifiedDate compare:reminder.dateModified] == NSOrderedDescending) { // This event has been modified // Most important now is to check if the changed event date has passed NSDate *today = [NSDate date]; if ([today compare:event.date] == NSOrderedDescending) { // Event date has passed if (reminder.isRepeating) { // We cancel the UILocalNotification and reschedule a new UILocalNotification for the next Reminder date status also set to overdue NSDate *reminderDate = [Utilities reminderDateFromDate:event.date andTime:reminder.date]; // Cancel UILocalNotification [Utilities cancelUILocalNotificationForReminder:reminder]; reminder.status = @(RMReminderStatusOverdue); reminder.date = reminderDate; reminder.dateModified = event.dateModified; reminder.dateServiceID = event.ID; reminder.dateText = event.name; NSDate *nextReminderDate = [Utilities nextReminderDateFromNowForReminder:reminder]; reminder.date = nextReminderDate; // Re-schedule the Reminder [Utilities scheduleUILocalNotificationForReminder:reminder]; // We change back to this old Reminder date to reflect the overdue status reminder.date = reminderDate; [[CoreDataHelper sharedInstance] saveInManagedObjectContext:self.syncContext]; } else { // We should cancel the UILocalNotification for this Reminder and set the status for this Reminder to overdue NSDate *reminderDate = [Utilities reminderDateFromDate:event.date andTime:reminder.date]; // Cancel UILocalNotification [Utilities cancelUILocalNotificationForReminder:reminder]; reminder.status = @(RMReminderStatusOverdue); reminder.date = reminderDate; reminder.dateModified = event.dateModified; reminder.dateServiceID = event.ID; reminder.dateText = event.name; [[CoreDataHelper sharedInstance] saveInManagedObjectContext:self.syncContext]; } } else { // Event date is in the future NSDate *reminderDate = [Utilities reminderDateFromDate:event.date andTime:reminder.date]; // Cancel UILocalNotification [Utilities cancelUILocalNotificationForReminder:reminder]; reminder.status = @(RMReminderStatusUpcoming); reminder.date = reminderDate; reminder.dateModified = event.dateModified; reminder.dateServiceID = event.ID; reminder.dateText = event.name; [[CoreDataHelper sharedInstance] saveInManagedObjectContext:self.syncContext]; // Re-schedule the Reminder [Utilities scheduleUILocalNotificationForReminder:reminder]; } } } 

CoreDataHelper.m

 - (NSArray *)fetchEntity:(NSString *)entity predicate:(NSPredicate *)predicate andSortDescriptors:(NSArray *)sortDescriptors inManagedObjectContext:(NSManagedObjectContext *)context { DLogName() if (context == nil) { // Use default MOC context = self.managedObjectContext; } NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entity inManagedObjectContext:context]; NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:entityDescription]; if (predicate != nil) { [request setPredicate:predicate]; } if (sortDescriptors != nil) { [request setSortDescriptors:sortDescriptors]; } NSError *error = nil; NSArray *entities = [context executeFetchRequest:request error:&error]; if (entities == nil) { DLog(@"There was an error: %@", [error userInfo]); } return entities; } - (NSManagedObjectContext *)threadedManagedObjectContext { NSManagedObjectContext *threadedMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; threadedMoc.parentContext = self.managedObjectContext; return threadedMoc; } /** Returns the managed object context for the application. If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. */ - (NSManagedObjectContext *)managedObjectContext { if (_managedObjectContext != nil) { return _managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { //_managedObjectContext = [[NSManagedObjectContext alloc] init]; _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; } return _managedObjectContext; } - (void)saveInManagedObjectContext:(NSManagedObjectContext *)context { if (context == nil) { // Use default MOC context = self.managedObjectContext; NSError *error = nil; if (context != nil) { if ([context hasChanges] && ![context save:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. */ DLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } } else { NSError *error = nil; // First save (child) context if ([context hasChanges] && ![context save:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. */ DLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } // Then save parent context if ([self.managedObjectContext hasChanges]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. */ [self.managedObjectContext performBlock:^{ NSError *parentError = nil; [self.managedObjectContext save:&parentError]; if (parentError) { DLog(@"Unresolved error %@, %@", parentError, [parentError userInfo]); abort(); } }]; } } } 

不完全确定这是否适用于您,但我得到类似的错误。 我通过解决他们

  1. 使用NSPrivateQueueConcurrencyType而不是NSConfinementConcurrencyType从主线程处理。

  2. executeFetchRequest放在MOC的performBlockAndWait

所以在CoreDataHelper.m的fetchEntity方法中,你应该是这样的:

 [context performBlockAndWait:^{ NSError *error = nil; NSArray *entities = [context executeFetchRequest:request error:&error]; }]; 

这可能无法给出完整的画面,但这是我采取的方法

每个托pipe对象都需要自己的线程。 在同一个线程中,你可以反复使用同一个被pipe理的对象。 但不在同一个队列中。 (这是我关注的一个误解)背景队列可以有很多不同的线程,MOC需要对每个线程都是唯一的。

这是我使用的方法。

ManagedObjectContextHolder.h

 #import <Foundation/Foundation.h> @interface ManagedObjectContextHolder : NSObject + (ManagedObjectContextHolder*) threadContextHolder; @property (nonatomic, strong) NSManagedObjectContext *context; @property (nonatomic, strong) NSString *contextThreadMocGuid; @property (nonatomic, weak) NSThread *contextThread; @end 

ManagedObjectContextHolder.m

 #import "ManagedObjectContextHolder.h" @interface HSContextSaveHandler : NSObject @property (nonatomic, strong) NSArray *mocArray; - (void) saveHappened:(NSNotification*) saveNotification; @end @implementation HSContextSaveHandler @synthesize mocArray = _mocArray; int saveFinished = 0; - (MyAppDelegate*) appDelegete{ return (MyAppDelegate*) [UIApplication sharedApplication].delegate; } - (void) saveHappened:(NSNotification *)saveNotification{ if (saveNotification && saveNotification.userInfo){ NSArray *staticArray = [NSArray arrayWithArray:self.mocArray]; for (id item in staticArray) { if ([item isKindOfClass:[ManagedObjectContextHolder class]]) { ManagedObjectContextHolder *holder = item; if ([saveNotification object] != holder.context){ @try { [holder.context mergeChangesFromContextDidSaveNotification:saveNotification]; } @catch (NSException *exception) { HSLogBrute(@"<<<<<<<< MERGE CHANGES FROM CONTEXT DID SAVE NOTIFICATION >>>>>>>>\n%@",saveNotification); } } } } saveFinished = 3; } } @end @interface NSThread (mocGuid) - (NSString*) mocGuid; - (void) setMocGuid:(NSString*) mocGuid; @end @implementation NSThread (mocGuid) - (NSString *)mocGuid{ return [self.threadDictionary valueForKey:@"mocGuid"]; } - (void)setMocGuid:(NSString *)mocGuid{ [self.threadDictionary setValue:mocGuid forKey:@"mocGuid"]; } @end @implementation ManagedObjectContextHolder static NSMutableArray *_mocHolders; static HSContextSaveHandler *_mocSaveHandler; + (NSMutableArray*) mocHolders{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _mocHolders = [NSMutableArray arrayWithCapacity:5]; _mocSaveHandler = [[HSContextSaveHandler alloc] init]; _mocSaveHandler.mocArray = _mocHolders; [[NSNotificationCenter defaultCenter] addObserver:_mocSaveHandler selector:@selector(saveHappened:) name:NSManagedObjectContextDidSaveNotification object:nil]; }); return _mocHolders; } + (ManagedObjectContextHolder *)threadContextHolder{ NSThread *currentThread = [NSThread currentThread]; NSString *mocGuid = currentThread.mocGuid; ManagedObjectContextHolder *result = nil; NSMutableArray *removeList = [[NSMutableArray alloc] initWithCapacity:[self mocHolders].count]; NSLog(@"Context Holders Count %d",[self mocHolders].count); for (ManagedObjectContextHolder *item in [self mocHolders]) { if (mocGuid != nil && item.contextThread == currentThread && item.contextThreadMocGuid == currentThread.mocGuid){ result = item; } if (item.contextThread == nil) { [removeList addObject:item]; } } if (removeList.count > 0){ NSLog(@"Removing %d Context Holders for Nil Threads",removeList.count); [[self mocHolders] removeObjectsInArray:removeList]; } if (result == nil){ result = [[ManagedObjectContextHolder alloc] init]; result.contextThread = currentThread; if (mocGuid == nil){ mocGuid = [HSStaticContainer uuidAsShortString]; currentThread.mocGuid = mocGuid; } result.contextThreadMocGuid = mocGuid; result.context = [[NSManagedObjectContext alloc] init]; [result.contextcontext setPersistentStoreCoordinator:[self appDelegate].persistentStoreCoordinator]; [result.context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy]; [[self mocHolders] addObject:result]; } return result; } @synthesize context = _context; @synthesize contextThreadMocGuid = _contextThreadMocGuid; @synthesize contextThread = _contextThread; - (id)init{ self = [super init]; if (self) { NSLog(@"Creating a Managed Object Context Holder. Here is the Stack Trace.\r\r%@",[NSThread callStackSymbols]); } return self; } @end 
Interesting Posts