循环引用如何查(自动引用计数与循环引用)
循环引用如何查(自动引用计数与循环引用)
2024-11-05 05:07:15  作者:许仙白素贞  网址: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-11-05常言家养靚鱼挡煞聚气红色,代表好运鱼缸净尺码(长150厘米、宽38厘米、深82厘米)2018年安装此鱼缸,第一批鱼是“四季发财”,这几条是鹦鹉鱼和地图鱼以后,有经验了,再养几条热带鱼“小丑鱼”更漂亮!,。
  • 我的团长我的团人物名单(我的团长我的团播出十年)
  • 2024-11-05我的团长我的团播出十年电视剧《我的团长我的团》以独特的视角展现了中国军民浴血奋战、保家卫国的故事,被誉为最经典的抗日题材电视剧创作过程中,主创人员在遵循历史真实的原则上,注重精神和情感层面的构建和传递,使观众产生强烈的共情。
  • 化妆品标签规则(所有成分均需在标签中标注)
  • 2024-11-05所有成分均需在标签中标注(本报记者满雪)6月3日,国家药品监督管理局发布公告,发布《化妆品标签管理办法》(以下简称《办法》)公告明确,自2022年5月1日起,申请注册或者进行备案的化妆品,产品标签必须符合《办法》的规定和要求。
  • 键盘失灵进入bios方法(键盘失灵如何进入bios)
  • 2024-11-05键盘失灵如何进入bios在开机进入BIOS的时刻,不同电脑进入BIOS的键值不一样,主要看是什么牌子的电脑,可以上网查一下进入BIOS的键值选择集成外设,并选择上、下集成外设,然后输入找到一个选项与USB字下的集成外设一旦你。
  • 盘点熊出没光头强开过最贵的车(剧情中最炫酷的5辆车)
  • 2024-11-05剧情中最炫酷的5辆车熊出没中虽然讲述的是森林中的熊大熊二光头强等主角之间的故事,尽管如此在剧情中还有很多炫酷的车,今天小编就为大家介绍5辆车,不知道你记不记得它们,如果让你选择一辆车,你会选择那一辆?第五名:光头强的飞行。
  • 每日优鲜信息屏蔽(北京市消协约谈每日优鲜)
  • 2024-11-05北京市消协约谈每日优鲜来源:【温州广电-快点温州】热点北京市消协约谈每日优鲜针对近日“每日优鲜”无法正常经营引发大量消费者投诉的情况,8月4日下午,北京市消协约谈北京每日优鲜电子商务有限公司会上,北京市消协向北京每日优鲜电。
  • 歌手许我来生再爱你(歌手许将前路夜已至尽头)
  • 2024-11-05歌手许将前路夜已至尽头——我终于明白,夜已至尽头,有光在等我歌手许将(JeremyJiang),生于1998年12月1日,唱作歌手、音乐人三岁学习钢琴,十六岁学习声乐,并开始了他的创作生涯目前已拥有六十余首词作品,二十余首。
  • excel中最好用的几个公式(让你在工作中提升工作效率)
  • 2024-11-05让你在工作中提升工作效率在工作中,领导让你统计各项指标参数你还在一个个相加吗,想查找某个库存还在一个个数吗这些数据在EXCEL中用简单的公式就能帮你求出你想要的数据公式1:多组数据同时求和(配合快捷键使用"ALT&#。
  • 孩子学习三天打鱼两天晒网怎么办(家长对孩子经常说)
  • 2024-11-05家长对孩子经常说小时候,经常听到的话语:“你,你又在看电视?前两天刚跟我说了要好好学习,单元考试考个好成绩给我呢?”“买了溜冰鞋,玩几天又不学了,这样怎么能行呢?”“你每次都说今天开始,要先完成作业,再玩,哪次是做到。
  • 普通轮毂如何装饰的霸气点(想改装轮圈咋选)
  • 2024-11-05想改装轮圈咋选要说哪项改装可最简单改变整台车气质?毫无疑问是Roll低底盘,所谓“一低遮百丑”,另外就是换轮圈无疑了如何挑选一款合适自己爱车的轮圈呢?这个当然不仅仅是视觉美学上的话题,挑选轮圈时的重要参数同样重要,。
  • 洗澡用香皂好还是沐浴露好(洗澡用沐浴露好还是香皂好)
  • 2024-11-05洗澡用沐浴露好还是香皂好洗澡用沐浴露好还是香皂好?这个问题很简单:如果你有条件使用纯净水洗澡,那么,自然是用香皂更好;相反,如果银子不足,只能用自来水洗澡,那么,还是乖乖使用沐浴露好一些香皂香皂本质上是肥皂,是由动物或植物油。
  • 膝关节骨质增生怎样治疗轻度(膝关节骨质增生能治好吗)
  • 2024-11-05膝关节骨质增生能治好吗膝关节骨质增生能治好吗_膝关节骨质增生怎么治疗好膝关节骨质增生也称为增生性退行性关节炎,是一种多见于中老年人,且女性发病高于男性的骨科疾病罹患膝关节骨质增生的患者非常痛苦,要承受疼痛、僵硬、肿胀、活动。