Magical Record 检测到默认上下文没有变化

Posted

技术标签:

【中文标题】Magical Record 检测到默认上下文没有变化【英文标题】:Magical Record detect no changes in default context 【发布时间】:2015-03-03 01:08:59 【问题描述】:

我正在为我的应用程序使用 Magical Record,因此我可以利用他们的核心数据堆栈,该堆栈应该自动将我在工作线程上下文(核心数据线程)中的更改传播到默认上下文(主线程)。我一直在核心数据写入队列中创建和更新我的对象,一切正常。

然后我遇到了这个问题,即 Magical Record 能够将我的更改保存在我的工作上下文中,但是当它尝试保存到默认上下文时,它检测到没有任何更改,因此不保存。

我哪里做错了?在我的应用程序的其余部分,我以几乎相同的方式创建和更新,并且它可以工作。请帮忙。谢谢!

下面是相关代码:

在所有这些更改后未检测到更改:

dispatch_async(CoreDataWriteQueue(), ^
    if (self.person) 
        FTPerson *localPerson = [FTPerson fetchWithID:self.person.id];
        [localPerson setName:self.nameField.text];
        [localPerson trainWithImages:self.addedImages];
     else 
        FTPerson *newPerson = [[FTPerson alloc] initWithName:self.nameField.text andInitialTrainingImages:self.addedImages];
        FTGroup *localGroup = [FTGroup fetchWithID:self.group.id];
        [newPerson addGroup:localGroup];
    
    [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait];
);

我也尝试了 saveWithBlock 方法,但没有运气:

dispatch_async(CoreDataWriteQueue(), ^
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext)
        if (self.person) 
            FTPerson *localPerson = [FTPerson fetchWithID:self.person.id];
            [localPerson setName:self.nameField.text];
            [localPerson trainWithImages:self.addedImages];
         else 
            FTPerson *newPerson = [[FTPerson alloc] initWithName:self.nameField.text andInitialTrainingImages:self.addedImages];
            FTGroup *localGroup = [FTGroup fetchWithID:self.group.id];
            [newPerson addGroup:localGroup];
        
    ];
);

这里是我创建人和组对象的地方:

dispatch_async(CoreDataWriteQueue(), ^
        FTPerson *newPerson = [[FTPerson alloc] initWithName:@"test" andInitialTrainingImages:@[[UIImage imageNamed:@"test.jpg"]]];
    );

dispatch_async(CoreDataWriteQueue(), ^
        FTGroup *newGroup = [[FTGroup alloc] init];
        [self setGroup:newGroup];
    );

还有init方法:

- (id)initWithName:(NSString *)name andInitialTrainingImages:(NSArray *)images 
    NSManagedObjectContext *context = [NSManagedObjectContext     MR_contextForCurrentThread];
    self = [FTPerson MR_createInContext:context];
    self.name = name;
    self.id = [[NSUUID UUID] UUIDString];
    self.objectIDString = [[self.objectID URIRepresentation] absoluteString];
    self.trainingImages = [[NSMutableArray alloc] init];
    [context MR_saveToPersistentStoreAndWait];
    return self; 

- (id)init 
    NSManagedObjectContext *context = [NSManagedObjectContext MR_contextForCurrentThread];
    self = [FTGroup MR_createInContext:context];

    self.id = [[NSUUID UUID] UUIDString];

    self.didFinishTraining = NO;
    self.didFinishProcessing = NO;
    self.photosTrained = @(0);
    self.lastProcessedDate = [NSDate date];

    [context MR_saveToPersistentStoreAndWait];
    return self;

已修复

所以问题最终与魔法记录无关。 我错误地将对象添加到我的核心数据 NSSet。而不是让它成为一个 NSMutableSet 并做 addObject:,我应该有:

NSMutableSet *mutablePhotos = [self mutableSetValueForKey:@"photos"];
[mutablePhotos addObject:photo];

【问题讨论】:

【参考方案1】:

所以问题最终与魔法记录无关。我错误地将对象添加到我的核心数据 NSSet。而不是让它成为一个 NSMutableSet 并做 addObject:,我应该有:

NSMutableSet *mutablePhotos = [self mutableSetValueForKey:@"photos"]; [可变照片添加对象:照片];

【讨论】:

【参考方案2】:

我认为您在处理 Core Data 时不应该使用自己的调度队列。 NSManagedObjectContext 上的 -performBlock: 方法的全部意义在于 Core Data 负责在正确的队列上执行提供的块。

现在回答你的问题。首先,您使用的是 MagicalRecord 2.x 还是 3.0?如果是 2.x,请确保使用开发分支,或者获取最新版本(此时为 v2.3beta5)。最新的 dev 和 release 分支有很多改进。

其次,我认为在使用 Core Data 进行多线程时做两件事很重要:

    始终通过编辑您的方案并设置-com.apple.CoreData.ConcurrencyDebug 1 来打开并发调试 始终非常明确地说明您要使用的上下文。 MagicalRecord 团队不再推荐方法-MR_contextForCurrentThread 是有原因的。修改您的创建方法以将NSManagedObjectContext 作为参数,这样就不会怀疑创建它的上下文是什么。

尝试进行以下更改:

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext)
    if (self.person) 
        FTPerson *localPerson = [self.person MR_inContext:localContext];
        [localPerson setName:self.nameField.text];
        [localPerson trainWithImages:self.addedImages];
     else 
        FTPerson *newPerson = [[FTPerson alloc] initWithName:self.nameField.text andInitialTrainingImages:self.addedImages inContext:localContext]; 
        FTGroup *localGroup = [self.group MR_inContext:localContext];
        [newPerson addGroup:localGroup];
    
];

人:

- (id)initWithName:(NSString *)name andInitialTrainingImages:(NSArray *)images inContext:(NSManagedObjectContext*)context 
    self = [FTPerson MR_createInContext:context];
    self.name = name;
    self.id = [[NSUUID UUID] UUIDString];
    self.objectIDString = [[self.objectID URIRepresentation] absoluteString];
    self.trainingImages = [[NSMutableArray alloc] init];
    [context MR_saveToPersistentStoreAndWait];
    return self; 

对于 Person,我注意到您传入了一个图像数组,但您为新的 person 实体分配了一个空数组。你的意思是这样做吗?

组:

-(id)initWithContext:(NSManagedObjectContext*)context 
    self = [FTGroup MR_createInContext:context];

    self.id = [[NSUUID UUID] UUIDString];

    self.didFinishTraining = NO;
    self.didFinishProcessing = NO;
    self.photosTrained = @(0);
    self.lastProcessedDate = [NSDate date];

    [context MR_saveToPersistentStoreAndWait];
    return self;

【讨论】:

我重写了我的核心数据相关代码以使用本地上下文。然后我意识到我的问题实际上与我如何使用魔法记录无关。我没有正确地将对象添加到我的核心数据集中。但是您的提示可能使我免于将来在保存核心数据方面可能遇到的许多麻烦。所以还是非常感谢!

以上是关于Magical Record 检测到默认上下文没有变化的主要内容,如果未能解决你的问题,请参考以下文章

Magical Record 因此消息而崩溃

使用 Swift 3 在 Magical Record 中保存上下文时出错

Magical Record 的新手:上下文未在数据存储中创建行

Magical Record添加对象,不同的上下文错误

Magical Record IOS 目标 C. 我们应该创建啥上下文?

如何使用 Magical Record 从特定上下文中简单地删除一组实体?