gpt4 book ai didi

ios - 后台线程中的核心数据锁

转载 作者:IT王子 更新时间:2023-10-29 07:52:57 33 4
gpt4 key购买 nike

我正在锁定 Core Data。我真的不明白原因。因为我在后台线程中处理时正在创建后台 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];
}];

关于ios - 后台线程中的核心数据锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14043384/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com