循环引用如何查(自动引用计数与循环引用)
循环引用如何查(自动引用计数与循环引用)
2024-09-27 07:34:34  作者:许仙白素贞  网址:https://m.xinb2b.cn/know/psc433906.html

本文首发于公众号【程序员华仔】

------------------------

本文主要讲解 自动引用计数循环引用 这两个大问题。

对于自动引用计数,没有什么争议。

而对于循环引用,这里主要是讲Object-C语言下的循环引用, 因为据我了解,Swift语言下也有循环引用。这两者根本原因是一致的,但解决方法有很大的差异。 所以这里特别说明是Object-C语言下的循环引用。对于Swift下的循环引用,以后再讲解。

自动引用计数

概念

说自动引用计数之前,先说下引用计数,引用计数是苹果公司设计出来,用来跟踪和管理App的内存情况的一套机制。它的运行机制大概是,当创建一个类对象,引用计数就为1,retain一次,计数 1,release一次,计数-1, 当计数减为0,系统就释放该对象,内存也回收,实现了对App的内存管理。

引用计数又分为:手动引用计数(Manual Reference Count)自动引用计数(Auto Reference Count),前者简称MRC,后者为ARC

MRC在iOS开发前期使用。主要由开发人员来管理引用计数。即要用的时候,retain一次,要释放的时候release一次。直到引用计数为零,系统才会释放对象,回收内存。

ARC是后来才推出的内存管理机制,它简化了流程。引用计数不需要开发人员来管理和维护了,全由系统帮助完成。 简单的来说,ARC无须开发人员考虑内存的管理情况,它会在类对象被引用的时候,引用计数 1;在release的时候,引用计数-1;在类对象不再使用的时候,自动释放其占用的内存。让开发人员从复杂的内存管理中解脱出来,大大地提高了开发人员的效率。

显然地,ARC更好用。那这么好用的ARC,我们再深入讲下它的工作机制。

自动引用计数的工作机制

正如前面所说一样,每当创建一个新的类对象时,ARC就会分配一块内存来储存该对象的信息。内存中会包含对象的类型信息,同时引用计数器会 1 。

当其他对象引用这个类对象时,计数再 1,若一直引用,那引用计数就一直累加。

当引用的类对象设置为nil,内部实现就是调用release一次,引用计数就会-1。多个类对象设置为nil, 就会减少多个计数值。

当对象不再使用了,ARC 就会释放对象所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的对象,不会一直占用内存空间。当然它的判断条件就是引用计数是否为零。为零就释放内存。

当 ARC 释放了类对象,类对象所对应的方法和属性将不能再被调用。否则App就会崩溃。

自动引用计数内部实现机制

在引用计数的底层实现中,是通过维护一个引用计数表来跟踪和计算每一个对象的引用情况。其中表里的key为内存块地址的散列值,value为该对象的引用计数。

每引用一次,value的值就 1, 同样的release一次,value值就-1,只有value值为0,才会销毁该对象并从引用计数表移除该key和value。

上述的对象引用过程,其实就是对对象的强引用。之所以称之为“强”引用,是因为它会将对象牢牢地保持住,只要强引用还在,对象是不允许被销毁的。

在实际开发过程中,声明一个类对象,默认就是强引用。

Person *person1;

该声明等价于

__strong Person *person1;

自动引用计数的实践

下面的例子展示ARC的工作机制。

1.创建一个Person类,定义name属性。在init和dealloc函数上分别打印init和dealloc的日志。

#import <Foundation/Foundation.h>

@interface Person : NSObject {

}

@property(nonatomic,retain) NSString *name;

@end

@implementation Person

- (id)init {

self = [super init];

NSLog(@"Person is being init");

return self;

}

- (void)dealloc {

NSLog(@"Person is being dealloc");

}

@end

2.然后在ViewdidLoad函数中定义 三个对象

Person *person1;

Person *person2;

Person *person3;

由于三个对象没有赋值,所以为空的。

这三个对象是强引用关系,因为它们等价于

__strong Person *person1;

__strong Person *person2;

__strong Person *person3;

3.接着创建Person对象并赋值给person1。

person1 = [[Person alloc] init];

通过这一段代码创建了一个Person对象,并把对象赋值给person1。也就是说person1强引用了Person对象。

在引用计数表中,就实现了 1操作。

4.接着在把person1赋值给person2,person3。实现person2,person3的强引用。

person2 = person1;

person3 = person1;

注:在实际开发中不会这么分解写代码。这里只是方便解说。正常的写法是2,3,4一起完成,如下代码所示:

Person *person1 = [[Person alloc] init];

Person *person2 = person1;

Person *person3 = person1;

5.对person1和person2对象进行释放操作。

person1 = nil;

person2 = nil;

通过这两行代码操作,Person对象内存释放吗? 显然没有。因为person3还持有该引用。

6.最后,person3设置为nil, 引用计数为0,ARC 才会销毁Person对象,回收内存。

以上就是自动引用计数的使用过程。

类对象间的循环引用

在上面的例子中,ARC 会跟踪新创建的Person对象的引用计数,并且会在 Person对象不再被需要时销毁它。

然而,我们可能会写出一个类对象的引用计数永远不能为0 的代码。即如果两个对象互相持有对方,每个对象都让对方一直存在,这种情况就是所谓的循环引用。

下面以具体类来说明下。

1.新建两个类,Person和Car

Class Car;

@interface Person : NSObject {

}

@property(nonatomic,retain) NSString *name;

@property(nonatomic,retain) Car *car;

@end

Class Person;

@interface Car : NSObject {

}

@property(nonatomic,retain) NSString *color;

@property(nonatomic,retain) Person *person;

@end

上述Person类中有两个属性,name表示“Person”对应的姓名,car表示“Person”对应的一辆车。假设这“Person”对象只有姓名和车。

同样地,Car对象下,color和person属性,对应的就是车的款式(以颜色代替)和车主。

2.创建类对应的实例并给属性赋值。

Person * john = [[Person alloc] init];

Car * johnCar = [[Car alloc] init];

john.name = “John”;

john.car = johnCar;

johnCar.color = “red”;

johnCar.person = john;

以上代码,在两个对象被创建和赋值后,就表现了强引用的关系。对象john现在有一个指向Car对象的强引用(johnCar),而变对象johnCar 有一个指向 Person 对象的强引用(car)。如下图。


同样地,这种强引用关系就包含了循环引用。即john对象持有了johnCar,johnCar也持有了john。 如下图。


当我们要释放john 和johnCar时,引用计数并不会降为 0,对象也不会被 ARC 销毁,这样就导致了内存泄露。

john = nil;

johnCar = nil;

未释放对象。

解决类对象的循环引用

针对上面的问题,在OC中,主要使用弱引用来解决循环引用问题。

弱引用

弱引用不会对其引用的对象进行强引用,因而不会阻止 ARC 销毁被引用的对象。

这个特性阻止了引用变为循环引用。我们只要在声明属性或者变量时,在前面加上 weak 关键字,就表明这是一个弱引用。

在底层的实现上,系统维护着一个弱引用表,每一次声明弱应用变量或属性都会在这张表中登记, 当对弱引用变量赋值时,就在这张表中建立起弱引用与对象之间的关系。有多少次赋值,就会建立多少次弱引用关系。

由于弱引用不会持有所引用的对象,即使引用存在,对象也有可能被销毁。因此,ARC 会在引用的对象被销毁后自动将其弱引用赋值为 nil,这个操作是为了对象的安全。

这样,对于上述的john和johnCar的循环引用问题,我们只要一方使用弱引用,就可以解除循环引用的问题。如下图所示:


在person释放的时候,可以不用等待Car的持有关系,因为它是弱引用。这种方式就解决了循环引用的问题,避免了内存泄露。

最后说明下:

关于循环引用有:1.类对象的循环引用;2.委托的循环引用;3.block的循环引用三个场景。

这次主要介绍类对象的循环引用和解决方法。

对于委托的循环引用,根本原因和类对象一样的(相互持有),解决方法就是在声明处使用weak关键字就可以了。

对于block的循环引用,内容比较多,见我的另一文章【探究Block底层原理(三)】

  • 本能是什么意思(词语本能是什么意思)
  • 2024-09-28词语本能是什么意思本能,汉语词汇,拼音:běnnéng,指本身固有的、不学就会的能力本能包括保存自己和种族的驱力,它涉及性和攻击欲望这些力量是周期性的,并且能以不同的方式获得满足出处瞿秋白《饿乡纪程·绪言》:“我在这样。
  • 精油泡澡的好处(精油泡澡有什么好处)
  • 2024-09-28精油泡澡有什么好处缓解疲劳:日常生活中人们用精油泡澡,具有极好的缓解压力的作用,还可以缓解身体和心理的疲劳,从而提高人们的睡眠质量,有益于人们的身心健康促进代谢排毒:另外,精油泡澡还可以促进人体的血液循环和代谢,能帮助。
  • 我的世界建筑怎么盖简单(我的世界建筑颜色和增加层次)
  • 2024-09-28我的世界建筑颜色和增加层次首先在这里祝MC生日快乐其次就是介绍干货了娘化烈焰人来了,说明又有一个我的世界知识要被介绍到了(配图配的不好,见谅)本期给大家介绍我的世界美学的颜色和增加层次颜色色环当你在为建筑选择配色方案时,色环会。
  • 风光摄影大赛征稿启事(首届兴林杯全国摄影大展征稿启事)
  • 2024-09-28首届兴林杯全国摄影大展征稿启事|作者:中国摄影出版社|责任编辑:陈勇|来源:中国网图片中心伊勒呼里山远朓摄影:轶夫巍巍兴安,孕育亘古沧桑画卷莽莽林海,描绘秀美生态丹青大兴安岭位于黑龙江省最北部,是我国东北部的著名山脉大兴安岭拥有令。
  • 南充有几所高校(南充首个高校图书馆向市民免费开放)
  • 2024-09-28南充首个高校图书馆向市民免费开放中国网四川南充4月15日讯4月14日,伴随响亮的读书宣言,西华师范大学第四届图书文化节在该校图书馆开幕记者从开幕式上获悉,该校图书馆将从4月15日起免费向市民开放阅读市民只需持本人身份证与一寸照片,即。
  • 最新版红楼梦何时开拍(继新红楼梦以后)
  • 2024-09-28继新红楼梦以后《新红楼梦》我们知道,该剧于2008年5月26日开始拍摄,2009年9月9日杀青,2010年6月24日在青岛电视台首播,并于2010年9月2日在安徽卫视、北京卫视上星首播杨幂,张馨予,杨洋、白冰、李沁。
  • 吴谨言现代新剧(吴谨言新剧开播刘敏涛为她做配角)
  • 2024-09-28吴谨言新剧开播刘敏涛为她做配角过去的演员要想成名,靠的是出演一部部精品的影视剧积累起来的名气,如今生活在网络特别发达的时代,出演上一部热播剧就能成为一个爆红的演员,比如吴谨言就是这样的演员!其实吴谨言也不是刚出道的演员,出道好多年。
  • 混合的八宝粥如何做(八宝粥怎么做才好吃)
  • 2024-09-28八宝粥怎么做才好吃将核桃剥壳,浸泡在水中,30分钟后剥去外层薄膜将花生、莲子、红豆浸泡在水里,1小时后洗净,沥水桂圆去壳,糯米、黑米、枸杞洗净备用锅中放入红豆、花生、莲子、糯米、黑米、核桃仁、枸杞、桂圆和适量清水,大火。
  • 郴州汽车总站地点(郴州汽车总站的故事)
  • 2024-09-28郴州汽车总站的故事三国演义中有诸葛亮七擒孟获,现实中郴州汽车站也毫不逊色几经周折几度搬迁今天我们就来说一说郴州汽车总站的故事!郴州汽车站最早可以追溯到民国时期:1924年湘南善后督办唐生智提出修建衡阳到郴州的公路在修建。
  • lol各个符文详解(新版LOL符文有多简单)
  • 2024-09-28新版LOL符文有多简单S8季前赛将会把天赋和符文融合为一个全新的符文系统,在这个全新的系统中,共有5个系列的不同符文,而在未来的S8赛季中,每个玩家只会选择其中的2大类6个符文征战召唤师峡谷,总结起来一句话就是“五种流派选。