背景资料
我几乎完成了我的应用程序。一切都运行良好。然后客户要求登录应用程序(即必须记录所做的事情,响应是什么等的各个点......)。
该应用程序允许用户创建保存到核心数据中的“消息”。然后将消息单独上传到服务器。消息在主线程上创建并上传到NSOperation
后台线程的子类中。
它与NSOperation
我之前使用过的子类的模板相同并且有效。我正在为多线程核心数据做所有最佳实践。
应用程序的所有这方面工作正常。
我添加了应用程序的日志记录部分。我创建了一个名为的单例MyLogManager
和一个CoreData
名为LogEntry
. 实体很简单,它只有一个date
and text
。
代码
MyLogManager 里面的函数是...
- (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;
}
和
- (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];
}
NSOperation 主线程(它的一部分)......
- (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
方法只是......
- (void)mergeChanges:(NSNotification *)notification
{
[self.managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];
}
我试过将“newLogWithText”函数扔到另一个线程,甚至扔到主线程,但没有运气。
我现在正要尝试,但将合并的“waitUntilDone”更改为“是”。(刚刚注意到这一点)。(这不起作用)。
我有 90% 的把握这是由于同时写入不同的上下文和解决冲突,因为这是我第一次处理这个问题。如果我注释掉这个newLogWithText
函数,那么一切都会正常运行。
目前唯一的选择是废弃LogEntry
核心数据并将日志保存到内部的数组中,NSUserDefaults
但这感觉不对。还有其他方法吗?
编辑
我现在已经改变了它,所以它的用户NSUserDefaults
和它的工作没有问题。感觉就像一个 hacky 解决方案。