背景信息
我差不多完成了我的应用。一切都很完美。然后客户端要求登录应用程序(即必须记录已完成的内容,回复内容等的各个点......)。
该应用程序允许用户创建保存在核心数据中的“消息”。然后将消息单独上载到服务器。消息在主线程上创建,并上载到后台线程的NSOperation
子类中。
它与我之前使用的NSOperation
子类的模板相同并且有效。我正在为多线程核心数据做所有最佳实践。
该应用程序的所有这一面都运行正常。
我添加了应用的日志记录部分。我创建了一个名为MyLogManager
的单例和一个名为CoreData
的{{1}}实体。该实体非常简单,只有LogEntry
和date
。
代码
MyLogManager中的函数是......
text
反过来运行...
- (void)newLogWithText:(NSString*)text
{
NSLog(@"Logging: %@", text);
NSManagedObjectContext *context = [self context];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"LogEntry" inManagedObjectContext:context];
LogEntry *logEntry = [[LogEntry alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
logEntry.text = text;
logEntry.date = [NSDate date];
[self saveContext:context];
}
和
- (NSManagedObjectContext*)context
{
AppDelegate *appDelegate = (ThapAppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator];
return managedObjectContext;
}
NSOperation主线程(其中很好的部分)......
- (void)saveContext:(NSManagedObjectContext*)context
{
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserver:appDelegate
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(@"Unhandled error %@, %@", error, [error userInfo]);
abort();
}
[[NSNotificationCenter defaultCenter] removeObserver:appDelegate name:NSManagedObjectContextDidSaveNotification object:context];
}
问题
我上传消息的- (void)main
{
//create context and retrieve NSManagedObject using the NSManagedObjectID passed in as a parameter to operation
self.message.lastSendAttempt = [NSDate date];
[self startUpload];
[self completeOperation]; //This doesn't get run because the startUpload method never returns
}
- (void)startUpload
{
[[MyLogManager sharedInstance] logSendingMessageWithURLParameters:[self.event URLParameters]]; //this is a convenience method. It just appends some extra info on the string and runs newLogWithText.
//Do some uploading stuff here...
//The operation stops before actually doing the upload when logging to CoreData.
}
子类(在后台线程上)调用此NSOperation
函数,但它也会更新正在上传的消息。 newLogWithText
使用相同的方法来获取和保存核心数据上下文。 (即它会更新上次发送的日期,如果发送成功也会更新。)
这是我第一次尝试处理同步写入并保存到核心数据。
我没有收到任何错误,应用程序继续“正常工作”。但操作永远不会完成。我试图用断点调试它,但是当我使用断点时它可以正常工作。没有断点,操作永远不会完成,上传永远不会发生。然后它只是坐在那里阻止它所在的队列,并且不能发送其他消息。
在我的NSOperation
中(我知道这不是理想的地方,但它是新项目的默认设置而我没有改变它)appDelegate
方法只是......
mergeChanges
我已经尝试将'newLogWithText'函数抛弃到另一个线程,甚至没有运气的主线程。
我现在正准备尝试,但将合并的“waitUntilDone”更改为YES。 (刚注意到这一点)。 (这没用)。
我90%肯定这是由于同时写入不同的上下文和解决冲突,因为这是我第一次处理这个问题。如果我注释掉- (void)mergeChanges:(NSNotification *)notification
{
[self.managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];
}
函数,那么一切正常。
目前唯一的选择是从核心数据中删除newLogWithText
并将日志保存到LogEntry
内的数组中,但感觉不对。还有另一种方式吗?
修改
我现在已经更改了它,因此用户NSUserDefaults
并且它可以正常运行。它只是一个hacky解决方案。