PrivateQueueConcurrencyType

时间:2016-03-22 23:11:03

标签: ios swift core-data memory-leaks

我发现今天的内存泄漏在我的 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)")
      }
    }

  }

2 个答案:

答案 0 :(得分:1)

private lazy var privateManagedObjectContext: NSManagedObjectContext = {
  let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
  moc.parent = managedObjectContext // Set the receiver’s parent context
  return moc
}()

也许这会解决它。

答案 1 :(得分:0)

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

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

将私有MOC保存在其后台线程上,然后使用主MOC上的performBlock方法保存其上下文,这将在主线程上保存主MOC的上下文。 performBlock确保您使用正确的队列。以下是一个示例,对于它在Objective-C中表示道歉。

在我的设置中,所有更改都始终保存在私有MOC上,然后传播到主MOC。关于核心数据的好书,实际上在objc.io的Swift中,对不同的场景有许多例子和优点/缺点。

// 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;
}