ARC内存管理机制详解

ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数。有自动引用计数,那么就得有手动引用计数MRC(Mannul Reference Counting),前面已经提到过了MRC。那么在ARC模式下是不是意味着我们就可以一点也不用进行内存管理的呢?并不是这样的,我们还需要代码进行内存的管理。下面会结合着代码把OC中的ARC机制做一个详细的总结(欢迎大家批评指针,转载请注明出处 )。

在ARC机制下是少不了下面这些东西的:

1.关键字 __strong  默认值,表示只要有强引用指针指向该变量,则该变量会一直存在。

2.关键字__weak 弱引用,表示若没有任何强引用指针指向该变量,会自动将变量的值置为空,即nil状态。

3.关键字 __autoreleasing 用于标示自动释放的变量

4.__unsafe_unretained 不安全的弱引用,若没有任何强引用指针指向该变量,不会自动设为空,会成为野指针。

   关于Weak和Strong,看下图吧:

第一次接触ARC的小伙伴们看到上面的概念可能会一头雾水,上面说的是个啥?都是哪跟哪?不用着急,下面会有实例代码,结合着实例代码,然后再做一个总结,你就会有种豁然开朗的感觉。你就会明白,哦,原来ARC是这么一回事。好啦,废话少说,用代码讲东西才是王道,代码走起。(为了方便我们观察内存的释放情况,可以设置断点来单步运行)

为了做测试使用,我们建一个测试类,并重写dealloc方法来观察内存的释放情况,测试类如下;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#import <Foundation/Foundation.h>

@interface TestClass : NSObject

@property(nonatomic, strong) NSString *name;

@end

#import "TestClass.h"

@implementation TestClass

//dealloc在对象释放是回调用

-(void)dealloc

{

    NSLog(@"%@,对象被释放啦!", _name);

}

@end

一.__strong:  强引用,是ARC中变量声明的默认值,用大白话讲就是你手动分配的堆内存,如果没有指针指向这块内存,那么这块内存就会被回收

1.当声明变量为强引用时,对象的指针出栈时,如果该指针指向的内存空间没有别的指针指向他,就自动掉用dealloc方法释放堆内存测试代码如下:


1

2

3

4

5

6

7

8

9

10

11

//情况1.当指向内存的指针在出栈时,内存被释放

void strongTest1()

{

    //测试用的代码块

    {

        //默认为强引用的变量

        TestClass *obj1 = [TestClass new];

        obj1.name = @"obj1";

    }

    NSLog(@"在出上面的大括号时,指针变量被释放,堆分配的内存也会别立即释放");

}

代码运行结果:


1

2

2014-08-13 19:25:52.378 ARCDemo[4345:303] obj1,对象被释放啦!

2014-08-13 19:25:52.380 ARCDemo[4345:303] 在出上面的大括号时,指针变量被释放,堆分配的内存也会别立即释放

代码说明:从运行结果来看,出代码块后我们定于的指针变量会随着我们代码块的结束而释放,就没有指针指向我们分配的堆内存了,以为默认为strong,所以在ARC机制下会立即调用dealloc来释放堆内存。

2.给对象指针重写分配内存的情况,代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//情况2.当对象指针 指向其他内存时,原有的内存若没有指针指向他,就会被立即释放

void strongTest2()

{

    {

        TestClass * obj1 = [TestClass new];

        obj1.name = @"obj1";

        //给已经分配内存的指针在分配新的内存

        obj1 = [TestClass new];

        NSLog(@"经过上面一步会释放第一次分配的堆内存!");

        obj1.name = @"obj1_new";

    }

    NSLog(@"出大括号回释放第二次分配的内存");

}

代码运行结果:


1

2

3

4

2014-08-13 19:30:38.455 ARCDemo[4356:303] obj1,对象被释放啦!

2014-08-13 19:30:38.456 ARCDemo[4356:303] 经过上面一步会释放第一次分配的堆内存!

2014-08-13 19:30:38.457 ARCDemo[4356:303] obj1_new,对象被释放啦!

2014-08-13 19:30:38.457 ARCDemo[4356:303] 出大括号回释放第二次分配的内存

代码说明:我们先给strong类型的对象指针分配内存空间,然后再次分配内存空间,在第二次分配空间的时候,就没有对象指针指向原有的内存空间,所以在第二次分配空间之后就会把原有的内存空间给释放掉,在出代码块的时候,对象指针也会随着栈内存的释放而释放掉,也没有对象指针指向第二次分配的内存了,所以会被释放掉。

3.把对象指针置为空时,分配的堆内存会立即被释放掉。相应的代码如下:


1

2

3

4

5

6

7

8

9

10

void strongTest3()

{

    {

        TestClass * obj = [TestClass new];

        obj.name = @"obj";

        obj = nil;

        NSLog(@"把指针置空时,指针指向的内存空间会被释放");

    }

}

代码运行结果:


1

2

2014-08-13 19:42:34.827 ARCDemo[4373:303] obj,对象被释放啦!

2014-08-13 19:42:34.829 ARCDemo[4373:303] 把指针置空时,指针指向的内存空间会被释放

代码说明:把指向该内存空间的对象指针置空,就相当于没有指针指向该内存空间,所以在strong下会被立即释放。

4.把新的对象指针指向堆内存空间,然后把原有的指针进行置空

代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

//情况4.把新的对象指针指向堆内存空间,然后把原有的指针进行置空

void strongTest4()

{

    {

        TestClass * obj1 = [TestClass new];

        obj1.name = @"obj1";

        TestClass * obj2 = obj1;

        obj1 = nil;

        NSLog(@"obj1指向的内存不会被释放,因为还有obj2指向");

    }

}

运行结果:


1

2

2014-08-13 19:46:06.554 ARCDemo[4394:303] obj1指向的内存不会被释放,因为还有obj2指向

2014-08-13 19:46:06.556 ARCDemo[4394:303] obj1,对象被释放啦!

代码说明:当两个指针同时指向一块内存空间时,把原有的指针置为空,这块内存空间不会被释放的,因为还有其他的指针指向该内存空间。

二. __weak 归零弱引用:在若指针指向的内存被释放后,若引用的指针则会置零

归零弱引用:弱引用的指针指向强引用的内存时,是不影响其释放内存空间的,当弱引用指针所指空间被释放掉得时候,该弱引用指针会被置零。

代码如下


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//weak: 归零弱引用:在若指针指向的内存被释放后,若引用的指针则会置零

void weakTest()

{

    //定义弱引用指针

    __weak TestClass *obj1;

    {

        //默认为强引用

        TestClass *obj2 = [TestClass new];

        obj2.name = @"obj2";

        //弱引用指针指向obj2

        obj1 = obj2;

        NSLog(@"强制引用堆分配得内存空间被释放前obj1的地址为:%p", obj1);

    }

    NSLog(@"强制引用堆分配得内存空间被释放后obj1的地址为:%p", obj1);

}

运行结果如下:


1

2

3

2014-08-13 19:55:31.393 ARCDemo[4413:303] 强制引用堆分配得内存空间被释放前obj1的地址为:0x100201ea0

2014-08-13 19:55:31.395 ARCDemo[4413:303] obj2,对象被释放啦!

2014-08-13 19:55:31.395 ARCDemo[4413:303] 强制引用堆分配得内存空间被释放后obj1的地址为:0x0

代码说明:当出大括号时强引用指针会被释放掉,之前开辟的堆内存空间只有一个弱引用指针指向他,所以在ARC中会被自动释放,弱引用指针会置零。

三. __autoreleasing 自动释放,一般结合着@autoreleasepool使用。

1.自动释放修饰的指针所指向的内存空间会在自动释放池结束的时候会被释放,代码如下


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//情况1:自动释放类型和自动释放池配合,提前释放对象,会产生野指针

void autoReleaseTest1()

{

    //定义自动释放对象指针

    __autoreleasing TestClass *obj;

    //定义释放池

    @autoreleasepool {

        obj = [TestClass new];

        obj.name = @"obj";

    }

    //此时obj为野指针

    NSLog(@"obj_p = %p",obj);

}

代码运行结果:


1

2

2014-08-13 20:02:00.489 ARCDemo[4436:303] obj,对象被释放啦!

2014-08-13 20:02:00.490 ARCDemo[4436:303] obj_p = 0x100108f00

代码说明:自动释放池结束后,自动对象指针指向的内存空间会被释放,但上面的用法会产生野指针。

2.__autoreleasing结合着自动释放池会延迟内存空间的释放

代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

//情况2.自动释放类型和自动释放池配合,延迟对象的释放

void autoReleaseTest2()

{

    @autoreleasepool {

        __autoreleasing TestClass *obj;

        {

            obj = [TestClass new];

            obj.name = @"obj";

            obj = nil;

            NSLog(@"把自动释放对象在自动释放池里置空,其所指内存空间是不会被释放的!");

        }

        NSLog(@"出上面的大括号,只要不出自动释放池是不释放所指内存空间的!");

    }

}

运行结果:


1

2

3

2014-08-13 20:06:45.890 ARCDemo[4448:303] 把自动释放对象在自动释放池里置空,其所指内存空间是不会被释放的!

2014-08-13 20:06:45.892 ARCDemo[4448:303] 出上面的大括号,只要不出自动释放池是不释放所指内存空间的!

2014-08-13 20:06:45.892 ARCDemo[4448:303] obj,对象被释放啦!

代码说明:由运行结果可以看出即使把指向内存空间的自动释放类型的指针置空,其对应的内存空间不像强引用那样被直接释放掉,而是等到自动释放池结束后在释放,这就是延迟释放。

3.被自动释放类型的指针用过的内存空间,在自动释放池结束的时候一样会被释放掉。

代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//情况3:自动释放类型和自动释放池的配合,延迟对象释放有可能造成暂时性的内存泄露

void autoReleaseTest3()

{

    @autoreleasepool {

        __autoreleasing TestClass *obj;

        {

            obj = [TestClass new];

            obj.name = @"firstObj";

            NSLog(@"上面的内存空间会由于下面的操作造成暂时内存泄露");

            obj = [TestClass new];

            obj.name = @"secondObj";

        }

    }

    NSLog(@"一块释放了两个,上面分配的内存空间被自动释放类型的变量用过,出自动释放池时就会被释放");

}

代码运行结果:


1

2

3

4

2014-08-13 20:12:37.512 ARCDemo[4459:303] 上面的内存空间会由于下面的操作造成暂时内存泄露

2014-08-13 20:12:37.514 ARCDemo[4459:303] secondObj,对象被释放啦!

2014-08-13 20:12:37.514 ARCDemo[4459:303] firstObj,对象被释放啦!

2014-08-13 20:12:37.515 ARCDemo[4459:303] 一块释放了两个,上面分配的内存空间被自动释放类型的变量用过,出自动释放池时就会被释放

代码说明:上面的代码可能会引起内存泄露,因为如果第一次分配空间的时候如果我们往对象里加入的是一个视频,那么在第二次给自动释放类型的指针分配内存的时候,前面的内存空间不会被释放掉,直到自动释放池结束后两个内存空间才会被释放掉。

四,strong, autoreleasing,weak混在一起的使用情况

在weak中的例子,我们能得到weak和strong同指向一块内存空间,当strong的指针不指向该内存空间时,这块内存空间就可以被释放掉,而weak指针被置零。

内存空间只要有autoreleasing或者strong的指针所持有,就不会被释放

1.strong和autoreleasing的混用

(1).strong类型的指针指向自动释放的空间

代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

void strongVsAutorelease1()

{

    {

        //定义强引用对象的指针

        TestClass *obj;

        @autoreleasepool

        {

            //定义自动释放类型的对象

            __autoreleasing TestClass *obj1 = [TestClass new];

            obj1.name = @"obj1";

            //强引用对象的指针指向自动释放类型对象的内存空间

            obj = obj1;

        }

        NSLog(@"自动释放类型的对象内存空间不会被释放,因为有strong类型的指针指向他");

    }

    NSLog(@"出上面的大括号,强类型的指针被释放,其指向的内存地址也会被释放");

}

运行结果如下:


1

2

3

2014-08-13 20:31:27.592 ARCDemo[4537:303] 自动释放类型的对象内存空间不会被释放,因为有strong类型的指针指向他

2014-08-13 20:31:38.895 ARCDemo[4537:303] obj1,对象被释放啦!

2014-08-13 20:33:04.873 ARCDemo[4537:303] 出上面的大括号,强类型的指针被释放,其指向的内存地址也会被释放

运行结果说明:上面是先让自动释放类型的指针指向该内存空间,然后再使强类型的指针指向该内存空间,在出自动释放池的时候是不会释放该内存空间的,直到强引用指针被释放掉,才释放该内存空间。

(2).自动释放类型的指针指向strong类型的指针所分配的空间的情况


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

void strongVsAutorelease2()

{

    @autoreleasepool {

        //定义自动释放类型的对象指针

        __autoreleasing TestClass *obj;

        {

            //定义强引用类型的对象并分配内存

            TestClass *obj1 = [TestClass new];

            obj1.name = @"obj1";

            //自动释放类型的对象指针指向强引用类型内存空间

            obj = obj1;

        }

        NSLog(@"出上面的大括号,强引用类型指针指向的内存空间不会被释放,因为为还有指针指向改内存");

    }

    NSLog(@"堆分配的内存在出自动释放池的时候被释放了");

}

代码运行结果:


1

2

3

2014-08-13 20:47:55.259 ARCDemo[4591:303] 出上面的大括号,强引用类型指针指向的内存空间不会被释放,以为还有指针指向改内存

2014-08-13 20:47:55.261 ARCDemo[4591:303] obj1,对象被释放啦!

2014-08-13 20:47:55.261 ARCDemo[4591:303] 堆分配的内存在出自动释放池的时候被释放了

结果说明:当strong修饰的指针随着栈的释放而释放,但其指向的内存空间并没有被释放,因为他还被自动释放类型的指针所持有,所以在出自动释放池的时候才会被释放。

(3).strong 类型的指针会指向自动释放类型的空间内存,当strong指针被置空时该内存不会被释放。

?    ?代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//strong 类型的指针会指向自动释放类型的空间内存,当strong指针被置空时该内存不会被释放。

void strongVsAutorelease3()

{

    @autoreleasepool {

        //定义自动释放类型的对象指针并分配内存

        __autoreleasing TestClass *obj = [TestClass new];

        obj.name = @"obj";

        {

            //定义强引用类型的对象

            __strong TestClass *obj1 = obj;

            //自动释放类型的对象指针指向强引用类型内存空间

            obj1 = nil;

        }

        NSLog(@"当obj1值空是其指向的内存空间不会被释放");

    }

    NSLog(@"当出四栋释放池的时候,该内存空间会被释放");

}

?代码运行结果:


1

2

3

2014-08-14 09:08:33.311 ARCDemo[569:303] 当obj1值空是其指向的内存空间不会被释放

2014-08-14 09:08:33.313 ARCDemo[569:303] obj,对象被释放啦!

2014-08-14 09:08:33.313 ARCDemo[569:303] 当出四栋释放池的时候,该内存空间会被释放

2.弱类型和自动释放类型的混用

代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

//weak类型的指针指向自动释放的空间

void weakVsutorelease4()

{

    {

        //定义弱引用对象的指针

        __weak TestClass *obj;

        @autoreleasepool

        {

            //定义自动释放类型的对象

            __autoreleasing TestClass *obj1 = [TestClass new];

            obj1.name = @"obj1";

            //弱引用对象的指针指向自动释放类型对象的内存空间

            obj = obj1;

        }

        NSLog(@"自动释放类型的对象内存空间会被释放,因为只有weak类型的指针指向他");

        NSLog(@"%p", obj);

    }

    NSLog(@"出上面的大括号,指针已经被释放。");

}

代码运行结果:


1

2

3

4

2014-08-13 21:00:58.855 ARCDemo[4618:303] obj1,对象被释放啦!

2014-08-13 21:00:58.857 ARCDemo[4618:303] 自动释放类型的对象内存空间会被释放,因为只有weak类型的指针指向他

2014-08-13 21:00:58.857 ARCDemo[4618:303] 0x0

2014-08-13 21:00:58.858 ARCDemo[4618:303] 出上面的大括号,指针已经被释放。

代码说明:即使有弱引用类型的指针指向该内存空间在出自动释放池的时候,该内存空间也会被释放。弱引用的指针会被置零。

上面写了这么多来点总结性的东西吧:strong 修饰的指针指向的空间如果没有其他指针就会被释放掉(weak类型的不算), 自动释放类型的指针如果没有其他类型的指针指向该内存空间时,当自动释放池结束后就会释放。

时间: 2024-10-06 12:59:18

ARC内存管理机制详解的相关文章

Android内存管理机制详解 (zhuan)

http://www.2cto.com/kf/201212/175786.html 与windows内存区别 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理.主要特点是,无论物理内存有多大,Linux都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能.而Windows是只在需要内存时,才为应用程序分配内存,

memcached内存管理机制详解

    我们知道,memcached是一个内存缓存系统,因此对于内存的管理是需要使用者了解的.本文将对memcached的内存模型及管理机制做一个详细的描述. 基本概念 在开始之前,有必要先了解几个基本概念: 1.slab class:在memcached中,对元素的管理是以slab为单元进行管理的.每个slab class对应一个或多个空间大小相同的chunk.参考下图一. 2.chunk:存放元素的最小单元.用户数据item(key.value等)最终会保存在chunk中.memcach

Android 内存管理机制详解

??嵌入式设备的一个普遍特点是内存容量相对有限.当运行的程序超过一定数量时,或者涉及复杂的计算时,很可能出现内存不足,进而导致系统卡顿的现象.Android 系统也不例外,它同样面临着设备物理内存短缺的困境.对于已经启动过一次的Android程序,再一次启动所花的时间会明显减少.原因在于Android系统并不马上清理那些已经"淡出视野"的程序(比如你调用Activity.finish退出UI界面).它们在一定的时间里仍然驻留在内存中.这样做的好处是明显的,即下一次启动不需要再为程序重新

SAP 内存管理参数详解

ST02,这里是内存管理参数详解 初学SAP看到很内存管理参数会有一头雾水,不知如何解读,这里之接上货 这只是一个例子,实际设中,我们会通常直接设定以下: rdisp/roll_maxfs=125000 rdisp/roll_shm=125000 rdisp/PG_shm=312500 rdisp/PG_maxfs=312500 原文地址:https://www.cnblogs.com/tingxin/p/11958011.html

MRC、ARC内存管理机制

MRC下,oc内存管理遵循"谁创建.谁释放.谁引用.谁管理"的机制,当创建或引用一个对象时,需要向她发送alloc,copy,retain消息,当释放该对象时需要发送release消息,当引用计数为零的时候,系统释放该对象. ARC是自动引用计数,管理机制与手动机制一样,只是不再需要调用retain,release,autorelease,它会在适当的位置插入release和autorelease.使用ARC不代表不需要内存管理了,例如使用block时要避免循环引用,代理作为属性时,要

Cocos2d之Ref类与内存管理使用详解

一.简介 用C++和JAVA编写过程序的朋友一定会为两种语言不同的内存管理机制懊恼.JAVA程序运行在JVM之上,由JVM自动实现内存管理,开发者只管申请内存而不用手动释放内存.当JAVA中对象没有被任何引用变量(类似于C和C++的指针)引用时,JVM会将对象释放掉.C++和C一样,是编译后能够直接被操作系统执行的语言,没有虚拟机负责其内存管理,因此需要在程序中管理内存.本文主要介绍如何使用cocos2d提供的内存管理机制. Cocos2d-x借鉴了“引用计数”思想,实现了一定程度上的自动内存管

iOS- 非ARC的项目内存管理细节详解(实战)

1.前言 接上文:iOS- 如何将非ARC的项目转换成ARC项目(实战) 2.内存管理时相关的配置 当我们把将非ARC的内存管理都管理好后,发现在做有些操作的时候内存还是在一直的缓慢增加 比如做一个最简单的随机数UITableView的显示与滑动,进行内存管理后,不应该出现内存增加的,但是一直滑动内存就一直缓慢的往上增加的情况. 这时候我们可以检查下看这里的属性是否打勾: 或者检测app一启动时控制台有没有立即输出下列这句话 如果勾上,上面三个选项,控制台就会出现下列几行输出 ARCTest(6

OC的MRC和ARC内存管理机制注意:Xcode4以前没有ARC。

1. Objective-c语言中的MRC(MannulReference Counting) 在MRC的内存管理模式下,对变量的管理相关的方法有:retain,release和autorelease.retain和release方法操作的是引用记数器,当引用记数为零时,便自动释放内存.并且可以用NSAutoreleasePool对象,对加入自动释放池(autorelease调用)的变量进行管理,当drain时回收内存. (1)retain,该方法的作用是将内存数据的所有权附给另一指针变量,引用

ucos内存管理原理详解

应用程序中为了某种特殊需要,经常需要动态的分配内存,而操作系统的特质置一,就是能不能保证动态内存分配的时效性,也就是说分配时间是可确定的 Ucos提供内存分配功能,它将内存空间分为两级管理,将一块连续的内存空间分为若干个分区,每个分区单位又分成大小相同的若干个内存块,分区时操作系统的管理单位,而内存块是分配单位,内存分区以及内存块的使用情况有一个叫做内存控制块的表来记录,内存控制块的基本结构如下 typedef struct os_mem { void   *OSMemAddr; void