【问题标题】:CoreData memory leak due to PrivateQueueConcurrencyType由于 PrivateQueueConcurrencyType 导致 CoreData 内存泄漏
【发布时间】:2016-07-10 01:56:47
【问题描述】:

我今天发现了一个内存泄漏,当我从我的 ma​​in NSManagedObjectContext 调用 executeFetchRequest 时,它就表现出来了。我终于发现,已知的违规者是因为我的 NSManagedObjectContext 将其父上下文分配给了私有托管对象上下文。

注释掉具有我的主要上下文的代码行分配了一个私有父类,而是直接指向 NSPersistentStoreCoordinator 释放我的应用程序的所有内存泄漏。

我正在阅读以下文章:http://martiancraft.com/blog/2015/03/core-data-stack/,了解如何在我的应用程序中实现 CoreData 的设计模式。我真的很喜欢让专用队列专门用于写入磁盘,并在使用 UI 时将主上下文作为调用的单一来源。

所以我的问题是,有没有其他人遇到过这个问题,如果有的话,您是否知道解决方法,而不是仅仅在一个上下文中工作以避免内存泄漏?

下面是我的 CoreDataStack 中显示两个上下文变量的部分。

private lazy var privateManagedObjectContext: NSManagedObjectContext = {
    let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    moc.persistentStoreCoordinator = self.persistentStoreCoordinator
    return moc
  }()

  lazy var managedObjectContext: NSManagedObjectContext = {
    let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)

    // Commenting out  #1 and unCommening #2 prevents the leak from happening
    // However when the reverse happens and we create the private context, a memory leak occurs first thing in the app.

    // #1
    // managedObjectContext.parentContext = self.privateManagedObjectContext

    // And instead replace it with this line
    // #2
    managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator


    return managedObjectContext
  }()

这是我使用主要和私有托管对象上下文的保存方法的内容。

func save() {
    guard managedObjectContext.hasChanges || privateManagedObjectContext.hasChanges else {
      return
    }

    print("Going to save now")

    managedObjectContext.performBlockAndWait() {
      do {
        try self.managedObjectContext.save()
      } catch {
        fatalError("Error saving main managed object context! \(error)")
      }
    }

    privateManagedObjectContext.performBlock() {
      do {
        try self.privateManagedObjectContext.save()
      } catch {
        fatalError("Error saving private managed object context! \(error)")
      }
    }

  }

【问题讨论】:

    标签: ios swift core-data memory-leaks


    【解决方案1】:
    private lazy var privateManagedObjectContext: NSManagedObjectContext = {
      let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
      moc.parent = managedObjectContext // Set the receiver’s parent context
      return moc
    }()
    

    也许这会解决它。

    【讨论】:

    • 这值得一试。感觉发布这个问题,我实际上使用 Realm 重构了它。但我有一个分支仍在使用 CoreData。在接下来的几天里,我会拉下那个分支,看看这是否能解决内存泄漏问题。
    • 在iOS10中,后台任务实际上现在执行的方式有所不同。尝试使用 persistentContainer.performBackgroundTask { } 自动为您提供背景上下文的方法。
    【解决方案2】:

    没有一种正确的方法,但这就是我在更新主线程上的主上下文时在后台保存数据的方式。

    将您的主 MOC(托管对象上下文)指定为 .MainQueueConcurrencyType。 将您的私有 MOC 指定为 .PrivateQueueConcurrencyType,并将其 parentContext 指定为主 MOC。

    将您的私有 MOC 保存在其后台线程上,然后在您的主 MOC 上使用 performBlock 方法保存其上下文,这会将您的主 MOC 的上下文保存在主线程上。 performBlock 确保您在正确的队列中。下面是一个例子,很抱歉它在 Objective-C 中。

    在我的设置中,所有更改总是保存在私有 MOC 上,然后传播到主 MOC。关于 Core Data 的好书,实际上是在 Swift 中objc.io 有很多例子和不同场景的优点/缺点。

    // Saving of your private and main context    
    [[self class] saveContext:self.yourPrivateContext];
    
    [self.yourPrivateContext.parentContext performBlock:^{
        [[self class] saveContext:self.yourPrivateContext.parentContext];
    }];
    
    
    +(BOOL)saveContext:(NSManagedObjectContext*)context{
    
        NSError *error = nil;
        if (context != nil) {  
            if ([context hasChanges] && ![context save:&error]) {   
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
                return NO;    
            } 
        }
        return YES;
    }
    

    【讨论】:

    • 您的回答完全有道理,这就是 Marcus Zarra 在他的文章中展示了如何做到这一点,我在我的问题中链接到了该文章。从我上面的代码sn-p可以看出,我的main的并发类型是main,私有上下文的并发类型是private。至于保存,我在问题中添加了我的保存方法。我相信它遵循您的建议。我开始认为这是 swift 中的一个错误。
    • 您将主 MOC 保存在私有 MOC 之前。我反其道而行之。您是对两种上下文都进行更改,还是只写一个上下文?
    • 只有在我保存到主文件后才写入私人文件。因为私有上下文在主推到它之前不会有任何上下文。此外,我只保存了应用程序委托中的 3 个位置:WillResign、WillTerminate 和 DidEnterBackground。所以我不确定当应用启动时,save 方法是如何导致内存泄漏的。
    • 感谢您的决心和帮助。寻找其他可能的原因,但已经在这上面住了 3 个晚上,但没有运气。幸运的是,我对 CoreData 的需求非常小,因此仅在 main 上节省并不会对性能造成太大影响。
    • 看起来不错。我唯一要注意的是,保存您的主 MOC 会将其更改推送到您的私有 MOC,这可能会覆盖其上下文中未保存的更改。
    猜你喜欢
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 2011-03-03
    • 2013-07-23
    • 1970-01-01
    相关资源
    最近更新 更多