0

背景资料

我几乎完成了我的应用程序。一切都运行良好。然后客户要求登录应用程序(即必须记录所做的事情,响应是什么等的各个点......)。

该应用程序允许用户创建保存到核心数据中的“消息”。然后将消息单独上传到服务器。消息在主线程上创建并上传到NSOperation后台线程的子类中。

它与NSOperation我之前使用过的子类的模板相同并且有效。我正在为多线程核心数据做所有最佳实践。

应用程序的所有这方面工作正常。

我添加了应用程序的日志记录部分。我创建了一个名为的单例MyLogManager和一个CoreData名为LogEntry. 实体很简单,它只有一个dateand 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 解决方案。

4

0 回答 0