- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在锁定 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
中,我在后台队列中进行一些核心数据处理。 Reminder
是 NSManagedObject
。抱歉代码量太大,但我认为最好不要遗漏任何重要细节。
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();
}
}];
}
}
}
最佳答案
不完全确定这是否适用于您,但我遇到了类似的错误。我通过
解决了它们使用 NSPrivateQueueConcurrencyType
而不是 NSConfinementConcurrencyType
使处理脱离主线程。
将 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/
从 0 开始搭建一套后台管理系统,成本巨大,所以都会选择一套成熟的组件库,基于此,再堆叠业务逻辑。我们公司的组件库基于 Ant Design。Ant Design 包含一套完整的后台解决方案,不仅
在我的 IOS 应用程序中,我有一个标记为 retain 的 NSDate* 属性 当我的应用程序再次激活时,属性值已被释放。 我是否误解了属性和内存管理的工作原理,我该如何防范? 最佳答案 很明显,
我有一个使用 BackgroundWorker 组件的示例 WinForms 应用程序。它工作正常,但是当我点击 Cancel 按钮取消后台线程时,它并没有取消线程。当我点击 Cancel 按钮调用
我目前正在开发一个应用程序,该应用程序在启动时会对服务器执行 ping 操作,该服务器会为每个连接的设备返回一个唯一标识符。设备每 5 秒从服务器检索另一页以获取一组不同的数据。这个唯一的 ID 可以
我正在开发一个应用程序,当它通过主页按钮在后台按下时,计时器应该启动,当应用程序返回前台并且计时器已经过了一定时间时,应该是执行。 我的问题是 当我的应用程序转到背景/前景? 是否有特殊的方法或其他技
我有 map View ,其中几乎没有 MKPointAnnotation。 一切正常,但是, View 的 MKPoiintAnnotation 的“背景”是“不可见的”,因此不是很“可见”。 我想
我在 iOS 中开发广告数据应用程序。我的应用程序广告数据在前台很好。但我想在 ios 后台宣传信标数据。我设置了背景外设设置。和广告数据 advertisingData = [CBAdvertise
如果我有一组操作,我想根据特定条件在后台工作程序中运行,例如,我有 10 个条件 if(a) BackgroundWorker doA = new backgroundworker() if(
我想独立运行一个函数。从我调用的函数中,我想在不等待其他函数结束的情况下返回。 我试过用 threadind,但这会等待,结束。 thread = threading.Thread(target=my
我想在用户在线时立即执行一些任务,即使他在后台也是如此。我正在使用 Reachability 类来检查互联网。但是当我在后台时,这个类没有通知我。我知道有人早些时候问过这个问题,但没有找到任何解决方案
我在后台播放文本转语音时出现间歇性(哎呀!)问题,由 Apple Watch 触发。我已经正确设置了后台模式、AVSession 类别和 WatchKitExtensionRequest 处理程序。
我有一个相当复杂的程序,所以我不会在这里转储整个程序。这是一个简化版本: class Report { private BackgroundWorker worker; public
我有一个任务在 backgroundworker 中运行。单击开始按钮,用户将启动该过程,并获得一个取消按钮来取消处理。 当用户点击取消时,我想显示一个消息框“进程尚未完成,你想继续吗”。 这里我希望
我有一个按以下方式编码的脚本。我想将它作为后台/守护进程运行,但是一旦我启动脚本,如果我关闭它从程序运行的终端窗口终止。我需要做什么来保持程序运行 loop do pid = fork do
我正在制作一个使用 ActivityRecognition API 在后台跟踪用户 Activity 的应用,如果用户在指定时间段(例如 1 小时)内停留在同一个地方,系统就会推送通知告诉用户去散步.
当尝试使用 URLSession 的 dataTaskPublisher 方法发送后台请求时: URLSession(configuration: URLSessionConfiguration.ba
当我编译这段代码时,我得到了他的错误,对象引用设置为null,错误位置在Dowork中,argumenttest.valueone = 8; public partial class Form1 :
有什么方法可以使用最小化或不活动的应用程序吗?我可以打开我的应用程序,然后打开并使用另一个应用程序,然后按一个按钮来激活我的程序吗? 例如,打开我的应用程序,打开 Safari,按下按钮(F1 或任何
我的具体要求是一个在后台运行的应用程序,被通知显示器即将进入休眠状态或者设备已经或即将达到空闲超时 - 然后唤醒并执行一些(简短的)一段代码。 我在这里找到了有关应用程序被置于后台或暂停的通知的引用:
我有一个 LSUIElement 设置为 1 的应用程序。它有一个内置编辑器,因此我希望该应用程序在编辑器打开时出现在 Cmd+Tab 循环中。 -(void)stepIntoForegrou
我是一名优秀的程序员,十分优秀!