副标题#e#
初学者在进修Objective-c的时候,很容易在内存打点这一部门陷入杂乱状态,很大一部门原因是没有弄清楚引用计数的道理,搞不大白工具的引用数量,这样就虽然无法彻底释放工具的内存了,苹果官方文档在内存打点这一部门说的很是简朴,只有三条准则:
当你利用new、alloc或copy要领建设一个工具时,该工具的保存指针为1,当不再利用该工具的时候,你应该想该工具发送一条release或autorelease动静,这样,该工具在其寿命竣事时将被销毁。
当你通过其他要领得到一个工具时,假设该工具的保存计数器为1,并且已经配置为自动释放,那么你不需要执行任何操纵来确保该工具被销毁。假如你规划在一段时间内拥有该工具,则需要保存它并确保它在操纵完成时释放它。
假如你保存了某个工具,就需要(最终)释放或自动释放该工具。必需保持retain要领和release要领的利用次数沟通。
假如在写代码的时候遵守这些准则,可以制止内存泄露,可是假如仅靠对这些准则的“影象”来写代码的话,恐怕本身心里都不会有底,一旦碰着问题阐明问题的时候很难从基础上找到问题呈现的原因,本文分享了本身在领略引用计数时的阐明进程,团结相关图形,但愿能让各人深刻领略工具引用计数的道理。
碰着了问题?阐明然后测试
当前工具的引用计数是几多呢?
为什么要提出这个问题,因为许多人会搞混工具的指针数量与引用数量的干系,不领略这个问题就弄不大白工具的引用计数到底为几多,虽然就无法正确释放内存了。在说这个之前先简朴相识一下堆内存与栈内存的观念,
栈内存:由编译器认真分派,存放局部情况中界说的根基变量的值,譬喻要领中的根基变量等,分开局部情况时会由编译器自动释放其内存空间。
堆内存: 一般由措施员通过new或alloc等来手动分派,利用完后也需要措施员手动释放,若措施员不释放,措施竣事时大概由OS接纳。留意它与数据布局中的堆是两回事。
变量名实际上是一个标记地点,在对措施编译毗连时由系统给每一个变量名分派一个内存地点。在措施中从变量中取值,实际上是通过变量名找到相应的内存地点,从其存储单位中读取数据。指针是一个非凡的变量,因为它存放的是一个变量的地点。如下图所示:
<img width="" height="" " src="http://img.ddvip.com/2014/0819/201408190838208644.png"/>
上面这个内存模子相信各人都知道,指针与工具存在一个间接(指向)的干系,因此当指针指向一个工具的时候,许多人就会以为这个指针引用到了该工具,进而就认为当指针指向一个工具的时候,该工具的引用计数就会加1,这种领略是一种感性的领略。实际上对付一个工具来说,它是不知道指向它的指针有几多个的,它的释放仅仅依靠的是引用计数,那么什么是引用计数呢?在objective-c中,大部门工具都担任于NSObject,NSObject包括一个用来生存引用数量的字段retainCount,说白了该字段就是引用计数,NSObject类的部门界说如下:
- (id)retain;
- (oneway void)release;
- (id)autorelease;
- (NSUInteger)retainCount;
- (NSString *)description;
因此,为了便于领略,我们可以把NSObject简化为如下模子:
#p#副标题#e#
工具可否释放就是判定其引用次数是否为零,也就是判定该工具的retainCount字段是否便是0,而指向该工具指针数量跟该工具retainCount字段的值并没有干系,因此指针数量并不便是引用数量,当指针指向该工具的时候,仅仅是给该指针变量赋值了,并没有修改工具的retainCount值,因此,指针指向一个工具的时候,该工具的引用计数是没有改变的。
以上面那段代码为例,我们挪用Test工具的new要领的时候,会自动将retainCount的值配置为1,当我们将test1赋值给test2的时候,只是一个指针赋值,并没有修改工具的retainCount值,所以引用计数稳定,依旧为1。
测试用例:
Engine *engine1 = [Engine new]; NSLog(@"通过new动静建设工具engine1:"); //输出engine1指针地点 NSLog(@"engine1 address is %p.",engine1); //输出engine1的retainCount NSLog(@"engine1 retainCount is %lu",(unsigned long)[engine1 retainCount]); Engine *engine2 = engine1; NSLog(@"将指针engine1复制给指针engine2:"); //输出engine2指针地点 NSLog(@"engine2 address is %p.",engine2); //输出engine1的retainCount NSLog(@"engine1 retainCount is %lu",(unsigned long)[engine1 retainCount]); //输出engine2的retainCount NSLog(@"engine2 retainCount is %lu",(unsigned long)[engine2 retainCount]); Engine *engine3 = [engine1 retain]; NSLog(@"通过retain动静得到工具engine3:"); //输出engine3指针地点 NSLog(@"engine3 address is %p.",engine3); //输出engine1的retainCount NSLog(@"engine1 retainCount is %lu",(unsigned long)[engine1 retainCount]); //输出engine2的retainCount NSLog(@"engine2 retainCount is %lu",(unsigned long)[engine2 retainCount]); //输出engine3的retainCount NSLog(@"engine3 retainCount is %lu",(unsigned long)[engine3 retainCount]); [engine2 release]; NSLog(@"给工具engine2发送动静release"); NSLog(@"engine2 address is %p.",engine2); NSLog(@"engine2 retainCount is %lu.",(unsigned long)[engine2 retainCount]);
输出功效如下:
从上面的输出功效可以得出以下几点结论:
和本文一开始阐明得出的功效一样,通过指针赋值并不能改变工具的引用计数。
岂论是通过指针赋值照旧通过retain得到工具,它们都指向同一个内存地点,即:指向同一个工具
在工具的引用计数归零之前,所有指向它的指针都是可用的。通过某个指针发送release动静仅仅是让引用计数减一,该指针自己不会被销毁。
#p#分页标题#e#
因为这里需要输出引用计数,就没有回收ARC,所以会有一个小问题,那就是当退出局部情况的时候,纵然局部指针所指向的工具已被销毁,局部指针变量的值仍然没有改变,因此需要手动赋值为nil。假如回收ARC的话,会自动接纳内存并将指针赋值为nil。
总结
不管是直接通过指针赋值照旧通过retain可能copy来保存工具,城市增加指向工具的指针数量,这些指针都指向同一块内存地点,因为工具所分派的内存地点是稳定的。
指向工具的指针的几多跟引用计数没有任何关系,可是通过retain、copy或release可以改变工具的引用计数。
仅当引用计数为0时才会释放工具占用的内存空间。
哎,真是“落花有意流水无情”啊,哪怕再多的指针“爱上工具”,人家这辈子却也只认引用计数。
From:cnblogs 眼神与背影