资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

ios开发循环引用,ios定时器循环引用

iOS 循环引用导致内存泄漏

singleton是实例对象, self 持有 singleton , singleton持有Block, Block持有 self ,也可直接理解为 self 持有 singleton , singleton 持有 self,当self需要释放的时候,singleton是不需要释放的,但是 singleton 持有self 导致 self 不能被释放,因此,self 无法被释放,导致内存泄漏。

成都创新互联公司为企业级客户提高一站式互联网+设计服务,主要包括成都网站建设、成都做网站、成都App定制开发重庆小程序开发公司、宣传片制作、LOGO设计等,帮助客户快速提升营销能力和企业形象,创新互联各部门都有经验丰富的经验,可以确保每一个作品的质量和创作周期,同时每年都有很多新员工加入,为我们带来大量新的创意。 

一、Block 回调造成的循环引用

二、NSTimer 强持有self

解决办法,使用 __weak typeof(self)weakSelf = self; 弱化self,打破循环引用(必要的时候我们还需要在block内部声明 strongSelf ,为防止weakSelf因为某种原因在block里提前释放使得weakSelf=nil,变成野指针,导致后边再调用weakSelf 造成崩溃)。示例如下

Timer 销毁请看此文: iOS Timer 详解

你真的了解循环引用吗?

循环引用的实质:多个对象相互之间有强引用,不能释放让系统回收。

如何解决循环引用?

1、避免产生循环引用,通常是将 strong 引用改为 weak 引用。

比如在修饰属性时用weak

在block内调用对象方法时,使用其弱引用,这里可以使用两个宏

#defineWS(weakSelf)            __weak __typeof(*self)weakSelf = self;// 弱引用#defineST(strongSelf)          __strong __typeof(*self)strongSelf = weakSelf;//使用这个要先声明weakSelf

还可以使用__block来修饰变量

在MRC下,__block不会增加其引用计数,避免了循环引用

在ARC下,__block修饰对象会被强引用,无法避免循环引用,需要手动解除。

2、在合适时机去手动断开循环引用。

通常我们使用第一种。

循环引用场景:

1. 自循环引用

对象强持有的属性同时持有该对象

2. 相互循环引用

3. 多循环引用

1、代理(delegate)循环引用属于相互循环引用

delegate 是iOS中开发中比较常遇到的循环引用,一般在声明delegate的时候都要使用弱引用 weak,或者assign,当然怎么选择使用assign还是weak,MRC的话只能用assign,在ARC的情况下最好使用weak,因为weak修饰的变量在释放后自动指向nil,防止野指针存在

2、NSTimer循环引用属于相互循环使用

在控制器内,创建NSTimer作为其属性,由于定时器创建后也会强引用该控制器对象,那么该对象和定时器就相互循环引用了。

如何解决呢?

这里我们可以使用手动断开循环引用:

如果是不重复定时器,在回调方法里将定时器invalidate并置为nil即可。

如果是重复定时器,在合适的位置将其invalidate并置为nil即可

3、block循环引用

一个简单的例子:

由于block会对block中的对象进行持有操作,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,则会造成循环引用。

解决方案就是使用__weak修饰self即可

并不是所有block都会造成循环引用。

只有被强引用了的block才会产生循环引用

而比如dispatch_async(dispatch_get_main_queue(), ^{}),[UIView animateWithDuration:1 animations:^{}]这些系统方法等

或者block并不是其属性而是临时变量,即栈block

还有一种场景,在block执行开始时self对象还未被释放,而执行过程中,self被释放了,由于是用weak修饰的,那么weakSelf也被释放了,此时在block里访问weakSelf时,就可能会发生错误(向nil对象发消息并不会崩溃,但也没任何效果)。

对于这种场景,应该在block中对 对象使用__strong修饰,使得在block期间对 对象持有,block执行结束后,解除其持有。

一文弄懂iOS中的循环引用

1.自循环引用

2.相互循环引用

3.多循环引用

假如有一个对象,内部强持有它的成员变量obj,

若此时我们给obj赋值为原对象时,就是自循环引用。

对象A内部强持有obj,对象B内部强持有obj,

若此时对象A的obj指向对象B,同时对象B中的obj指向对象A,就是相互引用。

假如类中有对象1...对象N,每个对象中都强持有一个obj,

若每个对象的obj都指向下个对象,就产生了多循环引用。

1.代理

2.Block

3.NSTimer

4.大环引用

1.避免产生循环引用。

在使用代理时,两个对象,一个强引用,一个弱引用,避免产生相互循环引用。

2.在合适的时机手动断环。

1.__weak

2.__block

3.__unsafe_unretained 用这个的关键字修饰的对象也没有增加引用计数,和__weak在效果上是等效的。

对象B会强持有A,对象A弱引用B

__block在ARC和MRC中是不同的:

1.修饰对象不会增加其引用计数,避免了循环引用。

2.如果被修饰的对象在某一时机被释放,会产生 悬垂指针 ,再通过这个指针去访问原对象的话,会导致内存泄露,所以一般不建议用,__unsafe_unretained去解除循环引用。

NSTimer

假如VC中有个广告栏,需要1S中滚动一次播放下一个广告,我们会把这个广告栏的UI对象作为VC的成员变量,由VC进行强持有。

因为广告栏每隔1S需要滚动播放,则广告栏中会添加成员变量NSTimer并强引用,当分配定时回调事件之后,NSTimer会对广告栏的Target进行强引用,就产生了相互循环引用。

如果把对象对NSTimer的强引用改为弱引用,是无法解决问题的, 原因如下图:

因为当NSTimer被分配之后,会 被当前线程的Runloop进行强引用 ,

如果对象以及NSTimer是在主线程创建的,就会被主线程的Runloop持有这个NSTimer,所以即使我们在广告栏中通过弱引用来指向NSTimer,但是由于主线程中Runloop常驻内存通过对NSTimer的强引用,再通过NSTimer对对象的强引用,仍然对这个对象产生了强引用,此时即使VC页面退出,去掉VC对对象的引用,当前广告栏仍然有被Runloop的间接强引用持有,这个对象也不会被释放,此时就产生了内存泄露。

解决方法:NSTimer分重复定时器和非重复定时器

在定时器的回调方法中去调用 [NSTimer invalid] 同时将 NSTimer置为nil ,可以将Runloop对NSTimer的强引用解除掉,同时NSTimer也解除了对对象的强引用。

不能在定时器的回调方法中去调用[NSTimer invalid]以及将NSTimer置为nil操作,此时的解决方案是:

左侧是Runloop对NSTimer的强引用,右侧是VC对对象的强引用,

可以在NSTimer和对象中间 添加一个中间对象 ,然后由NSTimer对中间对象进行强引用,

同时中间对象分别对NSTimer和广告栏对象进行弱引用,那么对于重复对象而已,

当当前VC退出之后,VC就释放了对广告栏对象的强引用,

当下次定时器的回调事件回来的时候,可以在中间对象当中,判断当前中间对象所持有的弱引用广告栏对象是否被释放了,

实际上就是判断中间对象当中所持有的weak变量是否为nil,

如果是nil,然后调用[NSTimer invalid]以及将NSTimer置为nil,

就打破了Runloop对NSTimer的强引用以及NSTimer对中间对象的强引用

这个解决方案是利用了:当一个对象被释放后,它的weak指针会自动置为nil。

中间对象TimerWeakObject的实现

iOS开发循环引用

-- :表示弱引用。

- :表示强引用。

循环引用可以简单理解为对象A引用了对象B,而对象B又引用了对象A:A - B - A,此时双方都同时保持对方的一个引用,导致任何时候双方的引用计数都不为0,双方始终无法释放就造成内存泄漏。

当然不只是两个对象之间相互引用会形成循环引用,多个对象之间相互引用最终形成环同样会形成循环引用。

例如:A-B-C-....-X-B。

循环引用对 app 有潜在的危害,会使内存消耗过高,导致内存泄漏,性能变差和 app 闪退等。

block 、 delegate 、NSTimer

self.tableView.delegate = self;

如果 delegate使用strong修饰就会构成循环引用:self - tableView - delegate - self。

所以在定义delegate属性时使用weak便能解决这一问题:self - tableView -- delegate - self。tableView和delegate之间不是强引用,所以构不成循环。

规避delegate循环引用的杀手锏也是简单到哭:定义delegate属性时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong。

(1)并不是所有block都会产生循环引用,block是否产生循环引用是需要我们去判断的,例如

(2)self - reachabilityManager - block - self,才会产生循环引用,并且XCode给出了循环引用warning,例如

(3)解决block循环引用的方法是使用__weak修饰self,然后在block里使用被修饰后的weakSelf来代替self:

1、合适的时机启动和销毁 NSTimer

解决 NSTimer 的循环引用,我们首先会想到的方法应该是在 OneViewController dealloc 之前就销毁 NSTimer,这样循环就被打破了。

最简单的方法就是在 viewWillAppear 中启动 NSTimer,然后在 viewWillDisappear 中销毁 NSTimer,成对出现,绝对没有问题。

2、Effective Objective-C ”中的52条方法

计时器保留其目标对象,反复执行任务导致的循环,确实要注意,另外在dealloc的时候,不要忘了调用计时器中的 invalidate方法。

如何在 iOS 中解决循环引用的问题

循环引用,指的是多个对象相互引用时,使得引用形成一个环形,导致外部无法真正是否掉这块环形内存。其实有点类似死锁。

举个例子:A-B-C-....-X-B -表示强引用,这样的B的引用计数就是2,假如A被系统释放了,理论上A会自动减小A所引用的资源,就是B,那么这时候B的引用计数就变成了1,所有B无法被释放,然而A已经被释放了,所有B的内存部分就肯定无法再释放再重新利用这部分内存空间了,导致内存泄漏。

情况一:delegate

Delegate是ios中开发中最常遇到的循环引用,一般在声明delegate的时候都要使用弱引用weak或者assign

@property (nonatomic, weak, nullable) id UITableViewDelegate delegate;

当然怎么选择使用assign还是weak,MRC的话只能用assign,在ARC的情况下最好使用weak,因为weak修饰的变量在是否后自动为指向nil,防止不安全的野指针存在

情况二:Block

Block也是比较常见的循环引用问题,在Block中使用了self容易出现循环引用,因此很多人在使用block的时候,加入里面有用到self的操作都会声明一个__weak来修饰self。其实便不是这样的,不是所有使用了Block都会出现Self循环引用问题,只有self拥有Block的强引用才会出现这种情况。

所以一般在函数中临时使用Block是不会出现循环应用的,因为这时候Block引用是属于栈的。当栈上的block释放后,block中对self的引用计数也会减掉

当然不一定要Self对Block有直接的引用才会出现,假如self的变量B,B中有个Block变量,就容易出现这种情况,好的是在block出现循环引用的,xcode7会出现警告提示(之前版本不确定)。

情况三:NSTimer

这是一个神奇的NSTimer,当你创建使用NSTimer的时候,NSTimer会默认对当前self有个强引用,所有在self使用完成打算是否的时候,一定要先使用NSTimer的invalidate来停止是否时间控制对self的引用

[_timer invalidate];

ios 单例 block 会循环引用 吗

出现循环引用的三种情况:

1.声明代理delegate属性

2.使用block时

3.使用NSTimer的时候

1.代理属性导致循环引用。

解决方案就是一定要记住,在声明delegate的时候修饰为weak(ARC)或者assign(MRC)

ARC环境下

//一代理属性为什么用weak,如果用strong的话会发生循环引用

//self -- person -- delegate -- self

// self.person = [[Person alloc] init];

// self.person.delegate = self;

MRC环境同理。

2.block导致的循环引用

这个比较复杂,我将它单独写在了一篇博文中.

block导致的循环引用问题的分析基解决办法

3.关于NSTimer导致的循环引用,我暂时不做讲解,准备充分时再补上。

NSTimer经常被作为某个类的成员变量,而NSTimer初始化时要制定self为target,容易造成循环引用。另一方面,若timer一直处于validate的状态,则其引用计数将始终大于0.


当前名称:ios开发循环引用,ios定时器循环引用
转载注明:http://cdkjz.cn/article/dscccdd.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220