【问题标题】:In what method should I recreate resources after didReceiveMemoryWarning?在 didReceiveMemoryWarning 之后我应该用什么方法重新创建资源?
【发布时间】:2023-04-03 19:50:01
【问题描述】:

我有一个视图控制器,它有一个私有的NSArray 变量。该变量在viewDidLoad 方法中初始化。调用didReceiveMemoryWarning 时会出现几个问题:

  1. 我应该将私有变量设置为nil吗?
  2. 如果我将其设置为nil,必须通过什么方法重新创建它?视图控制器是否调用viewDidLoad 方法重新创建它?

我问是因为视图的其他方法需要这个变量,如果它是 nil 将不起作用。

谢谢!

【问题讨论】:

  • 为什么要设置 NSArray *myArray;?为什么会调用 didReceiveMemoryWarning?如果它正在调用,则意味着您的代码中某处存在问题,您可以使用 ARC
  • @arc didReceiveMemoryWarning 在内存不足的情况下被调用。这并不一定意味着他的代码中有错误。该系统可能会发生很多事情。

标签: iphone ios memory-management uiviewcontroller


【解决方案1】:

创建延迟加载数据的自定义 getter。像这样的 sn-p 对非多线程环境有好处:

- (NSArray*) dataArray {
  if(_dataArray) return _dataArray;

  _dataArray = [self lordata];
  return _dataArray;
}

这样,如果您在内存警告中“释放”它们,数据总是会重新加载

【讨论】:

    【解决方案2】:

    通常,您通过 setter 分配 nil(例如 self.propertyName = nil)来卸载私有属性。或者您可以在调用 release 后将 ivar 设置为 nil,例如[_propertyName release]; _propertyName = nil;,但前者更可取。

    当内存不足时调用didReceiveMemoryWarning 方法。它在每个视图控制器上调用,包括负责当前可见 UI 的控制器!

    因此,当您接到didReceiveMemoryWarning 的调用时,您不能随意卸载数据——如果当前在显示器上可见,视图控制器可能需要该数据。

    一般原则是didReceiveMemoryWarning 可以摆脱它可以帮助释放内存的任何资源,但仅限于那些不是立即需要的资源。例如,在 OpenGL 游戏中,您不会卸载当前在显示器上可见的纹理。但是,请参阅我的最后一段。

    通常,您通过检查资源是否在需要时加载来重新加载资源,如果没有,则加载它们。

    不值得清理/释放微小的资源,例如单个正常大小的字符串。您应该专注于占用大量内存的项目。

    最近在后台内存管理方面取得的进步意味着您现在不太可能需要实际卸载数据 - 操作系统可以在后台卸载和重新加载未压缩的图像数据等。

    正如 Hot Licks 所提到的,模拟器有一个模拟内存警告的选项。值得在您的应用程序的不同点触发此内存警告以查看其行为方式。

    【讨论】:

    • 我会补充一点,模拟器允许您模拟此警告,以测试您的内存清理代码。
    • Apple 强烈建议使用惰性初始化模式重新加载清除的资源。添加一个简短的示例来说明如何做到这一点将改善您的答案。
    • Cheers Hot Licks,将其添加到我的答案中。
    【解决方案3】:

    例如,我有一个应用程序将数据下载到一个很长的表视图(可能有 1000 条记录)。为了支持这一点,我实现了一个“稀疏”数组,允许在引用时在网络上“出错”的空元素(下载时表格单元格中有一个“正在下载”指示器)。

    这是被操纵的,所以当 didReceiveMemoryWarning 发生时,数组将被清除,使用最近最少使用的算法删除数组中最旧的 N%。恢复将是自动的——清空的单元格在被引用时会重新加载。

    并不是我推荐这个特定的方案,但请注意拥有大量数据的一般特征,有一种方法来“优先考虑”应该删除的内容,以及有一种“软”的方式来重新加载数据(理想情况下只重新加载在不久的将来需要的部分)。

    【讨论】:

      【解决方案4】:

      ViewDidLoad 方法仅在 ViewController 初始化时调用一次。如果您必须将一些数据重新加载到您的 NSArray,您应该在需要时调用自己的方法来执行此操作。

      如果代码的各个部分都使用了这个数组,也许你应该考虑重新设计你的代码结构,以避免在一个对象中大量集中数据。

      编辑:正如下面cmets中@occulus所指出的,它不是在初始化View时调用的,而是在ViewController加载View时调用的..我的错误

      【讨论】:

      • 不,viewDidLoad 在视图控制器加载视图时调用,而不是在初始化时调用(init 方法)。看起来差别很小,但有时却很重要。
      • 另外,“只有一次”不一定正确:旧版本的 iOS 可以在内存不足的情况下卸载视图,因此稍后会再次调用 viewDidLoad。这暴露了视图控制器初始化(实际上只有一次)与正在加载的视图之间的重要区别。
      • 你说得对,@oculus ...我想我写错了关于初始化和加载的内容...我将添加一个编辑来解决这个问题
      • 与其把错误信息保留在第一段,然后再收回,何不直接修正第一段呢?不那么混乱。
      【解决方案5】:

      最好将变量设置为 nil。我的意思是释放它在didReceiveMemoryWarning 中的内存并设置一个脏标志。

      您可以随时检查数组 getter 中的脏标志(您可以自己编写)并重新填充它。这可能不是最好的方法。这完全取决于数组的使用情况。

      【讨论】:

      • 所以理论上如果我在didReceiveMemoryWarning 方法中将我的变量设置为nil,我将不得不检查nil 并重新填充,然后再尝试在我的任何其他方法中使用它。对吗?
      • 是的。你不想访问一个 nil 数组。
      • 投反对票?有什么原因吗?。
      • 谢谢!你的答案就是我想要的。
      • -1。不需要“脏”标志,只需在 getter 方法中检查 nil
      猜你喜欢
      • 2014-07-26
      • 2013-03-29
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-05
      相关资源
      最近更新 更多