即使在 ARC 下,在 GCD 中使用 Realm 时,我们是不是必须使用显式自动释放池

Posted

技术标签:

【中文标题】即使在 ARC 下,在 GCD 中使用 Realm 时,我们是不是必须使用显式自动释放池【英文标题】:Do we have to use explicit autorelease pool when using Realm in GCD even under ARC即使在 ARC 下,在 GCD 中使用 Realm 时,我们是否必须使用显式自动释放池 【发布时间】:2015-12-04 08:43:59 【问题描述】:

Realm 的文档中说:

在使用 Grand Central Dispatch 访问 Realm 时,您也可能会看到此问题。当 Realm 最终进入调度队列的自动释放池时,可能会发生这种情况,因为这些池在执行代码后的一段时间内可能不会被耗尽。在释放 RLMRealm 对象之前,不能重用 Realm 文件中的中间版本的数据。 为避免此问题,您应该在从调度队列访问 Realm 时使用显式自动释放池。

这是否意味着我们每次都必须在 GCD 中使用显式自动释放池,即使在 ARC 下也是如此?有人可以发布代码示例吗?这一点很重要,但官方文档并没有那么强调它

【问题讨论】:

我想说,每当你在后台线程上使用 Realm 时,这是必须的。 【参考方案1】:

您不必每次都使用显式的自动释放池。它更适用于您执行大量并发事务并且很容易遇到跟踪到许多即时版本的风险的场景。或者,当您希望通过释放所有打开的访问器来确保在应用程序的生命周期内关闭 Realm 文件时。

在这一点上,文档更多地被理解为了解技术限制并提示一旦遇到此类问题如何解决该问题,而不是一般的最佳实践。当然,你总是可以这样做,它不一定会伤害你(比如“如果你有一把锤子,一切看起来都像钉子。”),但你不一定非要这样做。

这究竟意味着什么的额外复杂性并不是每个人都需要的。了解显式自动释放池需要更深入地了解 ARC,这不是一般要求。如果您有想法,以及如何以更好的方式解决,我们非常欢迎您提供反馈。

Using a Realm Across Threads 部分给出了一个例子,在后台队列中插入一百万个对象:

dispatch_async(queue) 
  autoreleasepool 
    // Get realm and table instances for this thread
    let realm = try! Realm()

    // Break up the writing blocks into smaller portions
    // by starting a new transaction
    for idx1 in 0..<1000 
      realm.beginWrite()

      // Add row via dictionary. Property order is ignored.
      for idx2 in 0..<1000 
        realm.create(Person.self, value: [
          "name": "\(idx1)",
          "birthdate": NSDate(timeIntervalSince1970: NSTimeInterval(idx2))
        ])
      

      // Commit the write transaction
      // to make this data available to other threads
      try! realm.commitWrite()
    
  

在单独的自动释放池中创建类似数量的对象通常是有意义的,因为您无法使用 ARC 真正预测对象何时释放,因此您有一个明确的时间点,它们何时会最晚发生,这使您的程序更确定性地理解您和其他人。

【讨论】:

【参考方案2】:

为避免此问题,您应该在从调度队列访问领域时使用显式自动释放池。

这是否意味着我们每次都必须在 GCD 中使用显式自动释放池,即使在 ARC 下也是如此?

我不同意当前接受的答案,在任何后台线程(尤其是 GCD 等线程池)上,您应该始终在不再需要 Realm 实例时尽快强制关闭它,以避免版本保留。在 ios 中,可以使用 autoreleasepool ... 强制关闭 Realm 实例。

因此对于后台线程,通常建议始终使用显式自动释放池。

dispatch_async(queue) 
  autoreleasepool 
    let realm = try! Realm()
    //...
  


最好还是尽量减少您从后台线程提交的事务数,因此您应该尝试使用 1 个事务而不是 N。

// Break up the writing blocks into smaller portions
// by starting a new transaction
realm.beginWrite()
for idx1 in 0..<1000 
  // Add row via dictionary. Property order is ignored.
  for idx2 in 0..<1000 
    realm.create(Person.self, value: [
      "name": "\(idx1)",
      "birthdate": NSDate(timeIntervalSince1970: NSTimeInterval(idx2))
    ])
  

// Commit the write transaction
// to make this data available to other threads
try! realm.commitWrite()

【讨论】:

以上是关于即使在 ARC 下,在 GCD 中使用 Realm 时,我们是不是必须使用显式自动释放池的主要内容,如果未能解决你的问题,请参考以下文章

IOS GCD(线程的 串行并发 基本使用)

对象的潜在泄漏,即使在 ARC 中?

单例模式——使用GCD实现单例模式 & 非ARC单例模式 &使用GCD和线程锁实现单例模式-b

[ARC126C]Maximize GCD

arc159b

记得在所有Realm实例上调用close(),即使我调用close()