使用CoreData在两个队列之间传递对象引用的最佳做法是什么?

问题描述 投票:1回答:2

我正面临着应用程序架构设计的决定。

应用程序使用CoreData来保存用户信息,相同的信息也存储在REST接口可访问的远程服务器上。当应用程序启动时,我提供要显示的CoreData的缓存信息,同时从服务器获取更新。获取的信息也会自动保留。

所有这些任务都在后台队列中执行,以便不阻塞主线程。我正在强烈提及我的persistenContainer和我的NSManagedObject,名为User

@property (nonatomic, retain, readwrite) User *fetchedLoggedInUser;

正如我所说,填充用户通过执行获取请求

[_coreDataManager.persistentContainer performBackgroundTask:^(NSManagedObjectContext * _Nonnull context) {
        (...)
        NSArray <User*>*fetchedUsers = [context executeFetchRequest:fetchLocalUserRequest error:&fetchError];
        (...)
        self.fetchedLoggedInUser = fetchedUsers.firstObject;
        //load updates from server
        Api.update(){
            //update the self.fetchedLoggedInUser properties with data from the server
            (...)
            //persist the updated data
            if (context.hasChanges) {

            context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
            NSError *saveError = nil;

            BOOL saveSucceeded = [context save:&saveError];

            if (saveSucceeded) {

                //notify the App about the updates
                //**here is the problem**
            }
        };
}];

所以关于这一点的显而易见的是,在执行backgroundTask之后,我的self.fetchedLoggedInUser不再存在于内存中,因为它对我的PersistentContainer的performBackgroundTask()提供的NSManagedObjectContext的弱引用。因此,如果我尝试从另一个模型访问信息,则值为nil。每次我想要访问其值时,将获取的ManagedObject保留在内存中而不必再次获取它的最佳做法是什么?

A)在Documentation中,Apple建议使用ManagedObject的objectID,在队列之间传递对象传递队列之间的引用

NSManagedObject实例不应在队列之间传递。这样做可能会导致数据损坏和应用程序终止。当需要将托管对象引用从一个队列切换到另一个队列时,必须通过NSManagedObjectID实例来完成。

通过调用NSManagedObject实例上的objectID方法来检索托管对象的托管对象ID。

该情况的完美工作代码将替换if(saveSucceeded)检查此代码:

if (saveSucceeded) {

   dispatch_async(dispatch_get_main_queue(), ^{
      NSError *error = nil;
      self.fetchedLoggedInUser = [self.coreDataManager.persistentContainer.viewContext existingObjectWithID:_fetchedLoggedInUser.objectID error:&error];
      (...)
      //notify the App about the updates
   });
}

但我认为这可能不是最好的解决方案,因为这需要访问mainQueue上的mainContext(在本例中为persistentContainer的viewContext)。这可能与我在这里尝试做的事情相矛盾(在后台执行,以达到最佳性能)。

我的其他选择(好吧,这些,我想出来的)就是

B)将用户信息存储在Singleton中,并在每次从CoreData获取信息并将其保存到CoreData时更新它。在这种情况下,我不需要担心保持NSManagedObject上下文的活动。我可以对我的persistentContainer的performBackgroundTask提供的私有后台上下文执行任何更新,每当我需要保留新的/编辑的用户信息时,我可以从数据库中重新获取NSManagedObject,设置属性,保存我的上下文,然后更新Singleton。我不知道这是否优雅。

C)编辑我的self.fetchedLoggedInUser的getter方法以包含一个获取请求并获取所需的信息(这可能是最糟糕的,因为访问数据库时的开销)并且我甚至不确定这是否会起作用。

我希望其中一个解决方案实际上是最好的做法,但我想听听你的建议为什么/如何或为什么/如何不处理信息的传递。

TL:DR;当加载和存储新信息主要是从backgroundQueues完成时,最好的做法是在整个应用程序中保持用户信息的可用性?

PS:我不希望每次需要在我的一个ViewControllers中访问它时获取信息,我想将数据存储在中心结上,以便可以轻松地从每个ViewController访问它。目前,self.fetchedLoggedInUser是整个申请中使用的单身人士的财产。我发现这可以节省大量冗余代码,使用Singleton可以使信息的加载和存储更加清晰,并减少对数据库的访问次数。如果这被认为是不好的做法,我很乐意与您讨论这个问题。

ios objective-c multithreading core-data
2个回答
2
投票

使用NSFetchedResultsController - 它们非常有效,甚至可以用于一个物体。 FetchedResultsController执行一次提取,然后监视核心数据以进行更改。当它改变时,你有一个它已经改变的回调。它也适用于任何核心数据设置。只要更改传播到主上下文(使用newBackgroundContextperformBackgroundTask或子上下文或其他),fetchedResultsController将更新。因此,您可以自由更改核心数据堆栈,而无需更改监控代码。

一般来说,我不喜欢保持指向ManagedObjects的指针。如果从数据库中删除该条目,则当您尝试访问它时,managedObject将崩溃。 fetchedResultsController始终可以安全地读取fetchedObjects,因为它会为您跟踪删除。

显然将NSFetchedResultsController附加到viewContext并且只从主线程中读取它。


1
投票

在我看来,我提出了一个非常优雅的解决方案。从一开始我就使用了名为sharedCoreDataManager的Singleton,我添加了一个属性backgroundContext,它被初始化为如此

self.backgroundContext = _persistentContainer.newBackgroundContext;
_backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
_backgroundContext.retainsRegisteredObjects = YES;

并由sharedCoreDataManager保留。我正在使用此上下文来执行任何任务。通过调用_backgroundContext.retainsRegisteredObjects我的NSManagedObject由backgroundContext保留,sharedCoreDataManager本身(就像我说的)由我的Singleton save()保留。

我认为这是一个优雅的解决方案,因为我可以随时从后台访问ManagedObject线程安全。我也不需要任何额外的类来保存用户信息。最重要的是,我可以随时轻松编辑用户信息,然后根据需要在我的backgroundContext上调用qazxswpoi。

也许我将来将它作为一个孩子添加到我的viewContext中,我会评估性能并最终更新这个答案。

您仍然欢迎提出更好的解决方案或讨论此主题。

© www.soinside.com 2019 - 2024. All rights reserved.