一· 使用CADisplayLink,NSTimer有什么注意点?
问题一解决方案其中创建定时器的方式有:NSTimer,CADisplayLink(可以用在卡顿检测(检测应用程序的帧率)及自定义动画中,它的调用频率和设备屏幕刷新频率一致),GCD(更加准时,因为它不依赖与runloop,直接基于内核实现,更加高效)
截屏2019-12-11下午6.05.54.png
答:(1)循环引用问题 (2)不准时的问题(runloop跑圈执行时间问题和runloop模式问题,default模式下滚动视图滚动的时候会影响到定时器)
解决方式
其实创建中间对象直接继承于NSProxy就行,NSProxy和NSObject是平级的,用它做消息转发的话 省掉了方法动态解析的这个流程。所以直接用它就行了
截屏2019-12-11下午7.31.15.png
建议使用GCD定时器!因为NSTimer和CADisplayLink都依赖于runloop,而runloop就是不停的跑圈,每跑一圈检查一下累计的时间,每次任务可能不一样那么跑完一圈花费的时间也不一样,比如这次跑完一圈0.7秒还没到一秒不执行定时器的方法,继续跑下一圈恰巧这圈任务较重 跑了0.4秒 这个时候再去执行定时器的方法就慢了,所以就不准确了。而GCD创建的定时器是不依赖于runloop的 是直接和系统内核挂钩的,所以也不会受滚动视图的影响
销毁timer的方式
[图片上传中...(image.png-3b932-1586956747605-0)]
二· 介绍下内存的几大区域
地址由低到高内存布局为:保留内存--代码段--数据段(数据段里依次是字符串常量,已初始化数据,未初始化数据)--堆区--栈区--内存区。
堆区(heap)一般由程序员分配释放
栈区(stack)— 由编译器自动分配释放
1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局(静态)区包含下面两个分区:
数据区:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。
BSS区:BSS段包含了程序中未初始化全局变量。
4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区—存放函数体的二进制代码。
iOS程序的内存布局。 注意:堆空间地址分配是越来越大就是由低到高(记忆辅助:堆需要一点一点堆大,所以地址分配是越来越大),而栈空间是由高到低内存布局:栈区:存储局部变量(不包含字符串常量),在作用域结束后内存会被回收
三· 讲一下你对iOS内存管理的理解
内存管理 weak和__unsafe__unretained都是弱引用,但是weak所指向内存被释放之后,weak所修饰的对象会自动置为nil;而 __unsafe__unretained ,assign修饰的会仍然指向原对象的内存地址,变成野指针。注意野指针不一定崩溃的,就比如if(person3)不会崩,若打印内存地址就会崩溃或者调用isKindof会崩溃四· autorelease对象在什么时机会被调用release?
截屏2019-12-12下午10.05.54.png截屏2019-12-12下午10.33.21.png
方法里有局部对象,出了方法后会被立即释放吗?
iOS系统在运行程序时,会自动创建一些线程,⚠️每个线程都默认拥有自动释放池。一个线程对应一个runloop(解释在runloop的模式里,有default和tracking,而commons相对与一个几何,一个线程对应唯一一个runloop,commons的模式其实时按照设定的标志符区切换的)
答:分两种情况:
(1)(不分ARC还是MRC)
- 在主线程的时候:如果局部对象是通过autorelease形式释放的话,那么它不是马上释放,因为它是由runloop控制,而是在局部变量所在的runloop循环中,等Runloop休眠之前调用release操作进行释放。
- 在子线程的时候,当没有开启runloop的时候,会在子线程结束的时候会出发AutoreleasePoolPage的tls_dealloc方法,清空aoturelease对象;
- 子线程开启的情况下,那就是在最近一次runloop执行事件循环时,释放autorelease对象;
(2)在 arc 下,编译器帮我们添加的代码是在出方法前调用 [obj release],所以就是出了作用域(方法)会立即释放
自动释放池是怎么管理对象的?
截屏2019-12-19上午3.46.47.png 截屏2019-12-19上午3.45.26.png
截屏2019-12-19上午3.45.41.png
五.内存泄漏问题
ARC虽然可以帮助开发者解决大部分内存管理问题,但对内存泄漏仍然束手无策。
5.1 内存泄漏的核心问题是循环引用,那么循环引用的场景都有哪些呢?
- 代理用strong修饰的话会循环引用,所以要用weak区修饰。
- Block与循环引用
- 定时器引起的内存泄漏
其中定时器的场景有:NSTimer,CADisplayLink,还有PerformSelctor延迟方法。
5.1.1 重点说一下performSelector...delay...造成的内存泄漏
问题描述:testPerformSelectorFunc在viewDidDisappear之后执行,也就是在页面返回disappear以后,dealloc方法并没有执行,控制器对象并没有真正销毁,造成了内存泄漏。而等达到10s时间的时候,虽然该控制器已经disappear了,但还会执行该页面的代码testPerformSelectorFunc方法,执行完这个方法,才执行了dealloc方法,对象才得到了释放,证明执行完performSelector方法后self.retainCount会对应减少(个人理解:点击POP之后,也就是说如果func方法还没执行,那么不会走dealloc方法,self不会释放,当执行完这个方法之后才会调用dealloc,对象才能真正被释放)。
- 为了避免这种场景的内存泄漏,那么需要取消未执行的延时函数,对象就可以马上释放,加上这句代码即可:
[NSObject cancelPreviousPerformRequestsWithTarget:self]
- 内存泄漏和内存溢出的区别?就是内存溢出是内存不够,常见的就是数组越界,就比如存一个long大的数,却给了一个int类型的存储空间。如果内存泄漏不注意,有可能导致内存溢出,因为大量的对象不被释放,存在内存中,会占用越来越多的空间
6.创建大量临时自动释放对象 造成内存爆增现象
问题?:不加自动释放池的话,V啥时候释放呢?
- 咱也不知道长眼睛干嘛,可能只是单纯的好看。。。看截图的第二段啊!在大多数情况下,开发者都不必手动创建自动释放池,,但是有一种情况除外,系统的释放池会在每次事件循环结束后清空,所以不加自动释放池的话,V会在系统的runloop循环结束后再做清空操作。所以这个时候由于瞬时有大量的对象生成所以会导致内存爆增。那么这个时候就需要我们手动创建自动释放池。
加了之后的好处就是下一个截图所示:当自动缓冲池结束后,其会向添加进自动释放池的所有对象发送release消息,也就是每次循环自动释放池子都会对V发送release,所以就会在短时间内临时变量瞬间在内存中爆满,不能及时得到释放。这也是性能优化中对内存占用的一种优化
网友评论