【iOS】didReceiveMemoryWarning实例方法

iPhone下每个App可用的内存是被限制的,如果一个App使用的内存超过20M,则系统会向该App发送Memory Warning(内存警告)消息,收到此消息后,App必须正确处理,否则可能出错或出现内存泄漏。

目录

    • 流程
    • iOS 6以上版本的App对内存警告处理方法
    • 相关方法
      • loadView
      • viewDidLoad
      • viewDidUnload
      • initWithCoder:
      • awakeFromNib
    • 结论


官方文档:

请添加图片描述

重写didReceiveMemoryWarning方法:

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.NSLog(@"didReceiveMemoryWarning");
}

请添加图片描述

流程

当应用可用内存过低导致系统发出内存警告的时候,便会触发didReceiveMemoryWarning方法。App收到内存警告会调用:

UIApplication::didRecieveMemoryWarning -> UIApplicationDelegate::applicationDidRecieveMemoryWarning

然后调用当前所有的viewController进行处理,因此处理的主要工作在viewController。
创建viewController时,执行顺序是loadView -> viewDidLoad
当收到内存警告时,didRecieveMemoryWarning会判断当前viewController的view是否显示在window上:

  1. 如果viewController未显示(在后台),会执行didRecieveMemoryWarning -> viewDidUnload,前者会自动将viewController的view及其所有子view全部销毁
  2. 如果viewController当前正在显示(在前台),则只执行didRecieveMemoryWarning ,viewController的view不会被销毁
  3. 当重新显示该viewController时,执行过viewDidLoad的viewController(即原来在后台)会重新调用loadView -> viewDidLoad

iOS 6以上版本的App对内存警告处理方法

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];  //super调用的此方法即使没有显示在window上(在后台),也不会自动的将self.view释放。//此处做兼容处理需要加上iOS 6.0的宏开关,保证是在6.0下使用的,6.0以前屏蔽以下代码,否则会在下面使用self.view时自动加载viewDidUnLoadif ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {//需要注意的是self.isViewLoaded是必不可少的,其他方式访问视图会导致它加载 ,在WWDC视频也忽视这一点。if (self.isViewLoaded && !self.view.window) { // 是否是正在使用的视图//codeself.view = nil;// 目的是再次进入时能够重新加载调用viewDidLoad函数。}}
}
  • iOS 6之前:viewDidUnloaddidReceiveMemoryWarning都会被调用。
  • iOS 6之后:viewDidUnload不会被调用didReceiveMemoryWarning依然被调用。系统会自动处理View相关的内存,我们不用担心。也就是说不再支持viewDidUnload了。
    官方文档的解释是:系统会自动控制大的View所占用的内存,其他小的View所占用的内存是极其微小的,不值得为了省内存而去清理然后在重新创建。 如果你需要在内存警告的时候释放业务数据或者做些其他的特定处理,你可以实现didRecieveMemory方法。

苹果官方给出的相关解释方案总是美好的,但现实往往是残酷的:

  1. 我们的工程是ARC的。
  2. 我们会在viewController里面强持有(strong)大量子View得成员变量
  3. 我们实现了大量的viewDidUnload函数来释放(2)里面持有的那个子View

让我们看看我们的代码到了iOS6以后会发生什么事情。因为所有的子View都是strong持有的,这样会导致,即使系统内存警告导致了View的回收,他们也不会被真正的释放。于是乎,我们的程序可能就在后台被系统频繁的杀死。

栗子🌰:

一个App有三个tab(选项卡界面元素,比如“首页”、“通知”和“消息”的tabs):tabA、tabB、tabC(都从viewController继承,并且都实现了didRecieveMemoryWarning)。当程序启动时,默认显示tabA,这时,tabA的viewDidLoad被调用,并且加载数据显示给用户,然后切换到tabB,B会重复A的加载过程。
这时系统产生了一个内存警告,tabA、tabB、tabC三个对象都会受到警告。

  • tabA对象:因为它已经不在当前UI显示了,所以满足[self.view window] == nil,相关View被释放。
  • tabB对象:正在显示,所有didReceiveMemoryWarning什么也不会干。
  • tabC对象:最悲惨,从来没有显示过,viewDidLoad从来没调用过,也没有显示过。然后有个self.view .这句的调用会导致一个结果,就是C对象的viewDidLoad会被调用一次,于是他的逻辑就是释放前先创建一次,然后再把自己释放,是不是很悲剧。(所以apple给的方案也不一定完美靠谱)。

iOS 6之后,应该做的:

  1. 不要把子View当成员变量来持有,使用tag来操作(其实不管在哪个版本最后都这么做)。
  2. 不需要实现viewDidLoad,由系统自己来控制相关的内存释放。
  3. 在需要的时候实现didRecieveMemory来释放一些业务数据减少内存的占用,不要操作UIView。

相关方法

loadView

永远不要主动调用这个函数。viewController会在View的属性被请求并且当前view值为nil时调用这个函数。
如果手动创建View,应该重写这个函数,且不要在重写的时候调用[super loadView]方法。
如果用Interface Building创建View并初始化viewController,那就意味着使用initWithNibName:bundle:方法,这时就不应该重写loadView函数。

这个方法的默认实现是这样:

  1. 寻找有关可用的Nib文件的信息,根据这个信息加载Nib文件(所以Nib的加载过程是在loadView中完成的)。
  2. 如果没有有关Nib文件的信息,默认创建一个空白的UIView对象,然后把对象赋值给viewController的主View。

所以,如果决定重写这个方法时,也应该完成上述步骤,并把子类的View赋给view属性(创建的View必须是唯一的实例,并且不被其他任何Controller共享),而且重写的这个函数不应该调用super,这也是为了保持主View与Controller的单一映射关系。

viewDidLoad

这个方法在Controller加载了相关的Views后被调用,而且跟这些Views存储在Nib文件里还是在loadView方法中生成无关。

这个方法的作用主要是进一步初始化Views。viewDidLoad通常负责的是View及其子View被加载进内存之后的数据初始化的工作,即视图的数据部分的初始化。在iOS 3.0以及更高版本中,你应该重载viewDidUnload方法来释放任何对View的引用或者它里面的内容(子View等等)。

其多数情况下是做Nib文件的后续工作。

viewDidUnload

iOS 6之前这个方法是viewDidLoad的对立方法,在程序内存欠缺时此方法被controller调用,来释放View以及View相关的对象。
通常情况下,未显示在界面的viewController是UINavigationController Push栈中未在栈顶的viewController,或是UITabBarController中未显示的子viewController。这些viewController都在内存警告事件发生时,让系统自动调用viewDidUnload方法。由于viewController通常保存着view以及相关对象object的引用,所以必须使用这个方法来放弃这些对象的所有权以便内存回收,但不要释放哪些难以重建的数据。
通常controller会保存Nib文件建立的Views的引用,但是也可能会保存着loadView方法创建的对象的引用。最完美的方法是使用合成器方法:self.myCustomView = nil;,这样合成器会release掉这个view,如果没有使用属性,那么得显式释放这个view。

iOS 6之后,由于viewDidUnload事件在任何情况下都不会被触发,所以苹果在文档中建议,应该将回收内存的相关操作移到另一个函数didReceiveMemoryWarning中。但是如果仅仅写成(以下为错误示例):

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];if (self.isViewLoaded && !self.view.window) {self.view = nil;}
}

iOS 6以后,不建议将view置为nil的原因如下:

  1. UIView有一个CALayer成员变量,CALayer用于将自己画到屏幕上。
  2. CALayer是一个bitmap图像的容器类,当UIView调用自身的drawRect时,CALayer才会创建bitmap图像类。
  3. 具体占内存的其实是一个bitmap图像类,CALayer只占48Bytes,UiView只占96Bytes。而一个 iPad的全屏UIView的bitmap类会占到12MB的大小。
  4. 在iOS6,当系统发出内存警告时,系统会自动回收bitmap类,但是不回收UIView和CALayer类。这样既能回收大部分内存,又能在需要bitmap类时,通过调用UIView的drawRect:方法重建。

苹果系统对上面的内存回收做了一个优化:

  1. 当一段内存被分配时,它会被标记成 “In Use”,以防止被重复使用。当内存被释放时,这段内存被标记为“Not in use”,这样有新的内存申请时,这块内存就可能被分配给其他变量。
  2. CALayer包括的具体的bitmap内容的私有成员变量类型为CABackingStore,当收到内存警告时,CABackingStore类型的内存会被标记为Volatile类型,表示这块内存可能再次被原变量使用。

这样,有了上面优化后,当收到内存警告时,虽然所有的CALayer所包含的bitmap内存被标记成volatile了,但是只要这块内存没有被复用,当需要重建bitmap内存时,可以直接被复用,避免了再次调用UIView的drawRect:方法。

简言之,iOS6之后,不需要做任何以前viewDidUnload的事情,更不需要把以前viewDidUnload 的代码移到didReceiveMemoryWarning方法中。

initWithCoder:

使用了Interface Building创建对象并实例化时会调用initWithCoder:方法。

Interface Builder (IB) 是 Xcode 内置的图形界面编辑器,它允许我们用可视化的方式设计用户界面。当你在 Interface Builder 中创建对象时,实际上你是在使用 .xib 文件或 .storyboard 文件,这两种文件都基于 XML 文件格式来描述界面布局和配置。

反序列化是一种将数据流(在这种情况下是 XML 文件形式)转化成对象的过程。对于 Interface Builder 使用的 .xib.storyboard 文件:

  1. 界面描述.xib.storyboard 文件包含了界面布局的描述和对象模型,例如视图(controller)、视图(view)、约束等。

  2. 加载和解析:当你的项目加载并运行界面时,这个描述是被应用的。系统将解析这些文件中的 XML 描述,并将它转换成实际的运行时对象。

  3. 系统完成解析:这个过程由系统自动完成,开发者仅需要在 Interface Builder 中定义界面并保存文件,系统会负责剩余的工作。

因此,使用Interface Builder创建对象的过程,实际上就是定义.xib.storyboard文件的内容。随后,系统的运行时会处理这些文件,反序列化文件内容,从而创建相应的对象。开发者不需要手动进行这些反序列化工作,它作为加载过程的一部分已经被抽象掉了。

awakeFromNib

Interface Building创建的对象被实例化的时候调用。
initWithCoder:被调用之后,也会调用awakeFromNib。但是awakeFromNib相对于initWithCoder: 有个优势,后者在调用时虽然subViews已经被添加到图层中去了,但是还没有引用。而在awakeFromNib调用时,各种IBOutlet也都连接好了。

IBOutlet是Objective-C中的一种语言特性,它是用于连接Interface Builder(IB)与静态类型对象的一个标记与工具。它允许开发者在用户界面设计软件中设计元素,如按钮、标签等,然后在码中引用这些元素以便编程时使用。

这里是IBOutlet的详细说明:

  1. Objective-C属性IBOutlet通常与@property一起使用,用于创建一个公开接口来访问私有的数据。

  2. 连接界面与代码:通过将 Interface Builder 中的对象链接到具有 IBOutlet 标准的属性上,你可以在各个场景(scenes)之间分享信息或行为。

  3. 自动布局IBOutlet可以帮助Auto Layout视图或controller的布局。

  4. Xcode中可视编辑:当你在Xcode的Interface Builder中设计界面时,可以拖拽从你的视图到你的代码文件(如ViewController)上,自动创建IBOutlet

  5. 强引用:默认情况下,IBOutletstrong类型的引用,但可以在声明的时候使用不同的修饰符比如weak来避免循环引用。

结论

所以流程应该是这样:
(loadView/nib文件)来加载view到内存 ——>viewDidLoad函数进一步初始化这些view ——>内存不足时,调用viewDidUnload函数释放views —->当需要使用view时又回到第一步

UIViewController完整的生命周期:
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340420.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回归现实:无需复杂假设即可轻松评估过程能力的简单方法

Cpk 和 Ppk 等过程能力指标能够测量您的过程相对于客户规格要求的执行情况。我们先回顾一些能力分析基础知识,再深入了解另一个能力估计值 Cnpk,该估计值很可能在您的能力分析库中非常有用。 能力统计指标分析 能力统计指标使用单个数字,是一…

Linux命令 netstat -anp | grep 的用法

文章目录 1、第一种解释2、第二种解释3、第三种解释4、第四种解释5、第五种解释6、netstat --help 在Windows中,杀死端口占用的博客链接 1、第一种解释 在Unix和Linux系统中,netstat -anp 命令用于显示所有的网络连接( -a 表示所有&#xff…

OpenCv之简单的人脸识别项目(登录页面)

人脸识别 一、项目准备二、登录页面1.导入所需的包2.设置窗口2.1定义窗口外观和大小2.2设置窗口背景2.2.1设置背景图片2.2.2创建label控件 3.运行脚本3.1定义识别脚本3.2定义提取脚本3.3定义标注脚本3.4定义人脸比对脚本3.5定义动态处理脚本3.6定义属性判断脚本 4.创建一个退出…

浏览器阻止屏幕息屏,js阻止浏览器息屏,Web网页阻止息屏

场景: 比如打开一个浏览器页面(比如大屏),想让它一直显示着,而不是过几分钟不操作就屏幕黑了.(电脑有设置电脑不操作就会多长时间就会息屏睡眠,如果要求每个客户都去操作一下电脑设置一下从不睡眠,这很不友好和现实.而且我也只想客户在大屏的时候才这样,其他页面就正常,按电脑设…

如何从 Android 中恢复误删除的照片

尝试恢复 Android 智能手机上误删的文件、照片和视频可能非常麻烦,但无论如何,这是可以做到的。根据您用于删除照片的应用程序,方法可能因设备而异。但是,如果删除时间不长,您可能能够恢复它。在本文中,我们…

使用element的小弹框并修改css

使用el-popover来做弹框&#xff1a; 滑动或点击元素要加插槽slot"reference"来展示弹框&#xff1b; <el-popoverplacement"top"width"166"trigger"hover"popper-class"popover"><div><div><div>…

Docker中布置Jenkins实现Android项目的自动化构建

因项目需要&#xff0c;要在服务器上使用Jenkins完成Android项目的自动化构建&#xff0c;但服务器上登录的账户没有管理员权限&#xff0c;无法用sudo命令&#xff0c;因此需要把相应环境布置在docker中。 环境搭建 docker容器相关命令 创建容器 docker create -it contai…

防爆气象站解析

TH-FBCQX2&#xff08;FB02&#xff09;随着工业领域的快速发展&#xff0c;安全生产问题日益受到重视。特别是在石油化工、煤矿、烟花爆竹等易燃易爆环境中&#xff0c;准确、及时地获取气象数据对于预防事故、保障人员安全具有重要意义。防爆气象站作为这些特殊环境中不可或缺…

C#WPF数字大屏项目实战01--开发环境与项目创建

1、学习目标 -界面布局 &#xff0c;- 模板调整&#xff0c;- 控件封装&#xff0c;- 图表&#xff0c;- 通信对接&#xff0c;- 动态更新 2、开发环境 开发工具&#xff1a;Visual Studio-2022-17.8.6-Community 运行时框架&#xff1a;.Net 6或Framework 4.5以上 UI框…

QT入门知识回顾

1 QT简介 1.1 Qt模块: Qt Core模块: 是QT类库的核心&#xff0c;所有其他模块都依赖这个模块 Qt Gui模块: 提供GUI程序的基本功能 Qt Network模块:提供跨平台的网络功能 Qt Widgets模块:提供创建用户界面的功能 1.2Qt的signal/slot机制 任何一个类只要类体前部书写 Q_OBJ…

动手学深度学习4.8 数值稳定性和模型初始化-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;14 数值稳定性 模型初始化和激活函数【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址&…

网易云音乐格式在线转换

应用分享&#xff1a;众所周知网易云下载的格式为 .NCM&#xff0c;只能在网易云音乐里播放。 今天提供在线转换为MP3格式 NCM TO MP3&#xff0c;无需安装&#xff0c;转换后就能在任意播放器使用。 使用地址&#xff1a; https://ncm.worthsee.com/ 网络研究观 数据泄露…

E: Unable to locate package ros-kinetic-usb-cam

mkdir -p USB/src && cd USB/src catkin_init_workspace git clone https://github.com/bosch-ros-pkg/usb_cam.git cd .. catkin_make source devel/setup.bash echo "source ~/USB/devel/setup.bash" >> ~/.bashrc source ~/.bashrc 编译过程报错&…

【机器学习】机器学习在深度学习领域中的作用:半监督学习的视角

&#x1f440;时空之门&#x1f440; &#x1f50d;引言&#x1f388;半监督学习概述&#x1f69d;机器学习在深度学习领域中的作用☘特征提取与表示学习&#x1f340;复杂任务建模❀结合半监督学习提升性能 &#x1f680;半监督学习在深度学习中的应用场景&#x1f4d5;图像识…

你每天都在用的APP,原来都是Python写的!

&#x1f446;点击关注 获取更多编程干货&#x1f446; 要说Python的用途&#xff0c;那可太多了&#xff0c;也许你不知道Python是什么&#xff0c;但你一定用过它开发的产品&#xff0c;就像你可能不了解汽车引擎的构造&#xff0c;但你每天都享受着汽车带来的便利一样。 比…

【ARM+Codesys案例】树莓派+Codesys软PLC方案在包装行业灌装旋盖机的应用

ARM系列支持&#xff1a;全志T3、RK3568、树莓派 机型定义&#xff1a;双工位旋盖机 旋盖机主要适用于不同规格的材质及不同规格的盖、旋&#xff08;轧&#xff09;盖。适用螺旋盖、防盗盖、防撞盖、压入盖等。压力可方便调整&#xff0c;根据瓶盖大小设置取盖位。结构紧凑、…

pdf拆分成有图和无图的pdf(方便打印)

pdf拆分成有图和无图的pdf(方便打印) 原因 打印图片要彩印&#xff0c;每次都要手动弄&#xff0c;打印的时候很麻烦&#xff1b; 随着打印次数的增加&#xff0c;时间就越来越多 为解决此问题&#xff0c;使用python写一个exe解决这个问题 历程 找一个python的GUI界面找到 t…

GPT注册、手机验证码、侧边栏、翻译、绘图和视频的安装与使用

侧边栏 下面这个侧边栏收费 效果不错 ## chrome自动翻译 沉浸式翻译效果最好&#xff0c;支持视频 沉浸式翻译 微软网页 https://designer.microsoft.com/image-creator https://www.yeschat.ai/zh-CN/assistants 字节AI网页 https://www.coze.com/store GPT注册 https:…

qt+ffmpeg 实现音视频播放(四)之音视频同步

在处理音视频数据时&#xff0c;解码音频的数据往往会比解码视频的数据比较慢&#xff0c;所以我们在播放音视频时&#xff0c;音频和视频的数据会出现渐渐对不上的情况。尤其在播放时间越长的时候&#xff0c;这种对不上的现象越明显。 为了解决这一问题&#xff0c;人们想出…

数据挖掘综合案例-家用热水器用户行为分析与事件识别

文章目录 1. 背景与挖掘目标2. 分析方法与过程3. 数据分析3.1 数据探索分析3. 2 数据预处理1. 属性约束2. 划分用水事件3. 确定单次用水事件时长阈值4. 属性构造5.筛选候选洗浴事件 3.3 模型构建3.4 模型检验 4. 思考总结 1. 背景与挖掘目标 随着国内大家电品牌的进入和国外品…