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方法来观察内存的释放情况,测试类如下;

#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.当指向内存的指针在出栈时,内存被释放
void strongTest1()
{
    //测试用的代码块
    {
        //默认为强引用的变量
        TestClass *obj1 = [TestClass new];
        obj1.name = @"obj1";
    }
    NSLog(@"在出上面的大括号时,指针变量被释放,堆分配的内存也会别立即释放");
}

            代码运行结果:
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.给对象指针重写分配内存的情况,代码如下:

//情况2.当对象指针 指向其他内存时,原有的内存若没有指针指向他,就会被立即释放
void strongTest2()
{
    {
        TestClass * obj1 = [TestClass new];
        obj1.name = @"obj1";
 
        //给已经分配内存的指针在分配新的内存
        obj1 = [TestClass new];
        NSLog(@"经过上面一步会释放第一次分配的堆内存!");
 
        obj1.name = @"obj1_new";
    }
    NSLog(@"出大括号回释放第二次分配的内存");
 
}

            代码运行结果:
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.把对象指针置为空时,分配的堆内存会立即被释放掉。相应的代码如下:

void strongTest3()
{
    {
        TestClass * obj = [TestClass new];
        obj.name = @"obj";
 
        obj = nil;
        NSLog(@"把指针置空时,指针指向的内存空间会被释放");
    }
}

            代码运行结果:
2014-08-13 19:42:34.827 ARCDemo[4373:303] obj,对象被释放啦!
2014-08-13 19:42:34.829 ARCDemo[4373:303] 把指针置空时,指针指向的内存空间会被释放

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

 

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

            代码如下:

//情况4.把新的对象指针指向堆内存空间,然后把原有的指针进行置空
void strongTest4()
{
    {
        TestClass * obj1 = [TestClass new];
        obj1.name = @"obj1";
 
        TestClass * obj2 = obj1;
        obj1 = nil;
        NSLog(@"obj1指向的内存不会被释放,因为还有obj2指向");
 
    }
}

            运行结果:
2014-08-13 19:46:06.554 ARCDemo[4394:303] obj1指向的内存不会被释放,因为还有obj2指向
2014-08-13 19:46:06.556 ARCDemo[4394:303] obj1,对象被释放啦!

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

 

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

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

            代码如下

//weak: 归零弱引用:在若指针指向的内存被释放后,若引用的指针则会置零
void weakTest()
{
    //定义弱引用指针
    __weak TestClass *obj1;
    {
        //默认为强引用
        TestClass *obj2 = [TestClass new];
        obj2.name = @"obj2";
        //弱引用指针指向obj2
        obj1 = obj2;
        NSLog(@"强制引用堆分配得内存空间被释放前obj1的地址为:%p", obj1);
    }
    NSLog(@"强制引用堆分配得内存空间被释放后obj1的地址为:%p", obj1);
}

        运行结果如下:
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:自动释放类型和自动释放池配合,提前释放对象,会产生野指针
void autoReleaseTest1()
{
 
    //定义自动释放对象指针
    __autoreleasing TestClass *obj;
    //定义释放池
    @autoreleasepool {
        obj = [TestClass new];
        obj.name = @"obj";
    }
    //此时obj为野指针
    NSLog(@"obj_p = %p",obj);
 
}

        代码运行结果:
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结合着自动释放池会延迟内存空间的释放

        代码如下:

//情况2.自动释放类型和自动释放池配合,延迟对象的释放
void autoReleaseTest2()
{
    @autoreleasepool {
        __autoreleasing TestClass *obj;
        {
            obj = [TestClass new];
            obj.name = @"obj";
            obj = nil;
            NSLog(@"把自动释放对象在自动释放池里置空,其所指内存空间是不会被释放的!");
        }
        NSLog(@"出上面的大括号,只要不出自动释放池是不释放所指内存空间的!");
    }
}

        运行结果:
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.被自动释放类型的指针用过的内存空间,在自动释放池结束的时候一样会被释放掉。

        代码如下:

//情况3:自动释放类型和自动释放池的配合,延迟对象释放有可能造成暂时性的内存泄露
void autoReleaseTest3()
{
    @autoreleasepool {
        __autoreleasing TestClass *obj;
        {
            obj = [TestClass new];
            obj.name = @"firstObj";
 
            NSLog(@"上面的内存空间会由于下面的操作造成暂时内存泄露");
 
            obj = [TestClass new];
            obj.name = @"secondObj";
        }
    }
    NSLog(@"一块释放了两个,上面分配的内存空间被自动释放类型的变量用过,出自动释放池时就会被释放");
}

       代码运行结果:
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类型的指针指向自动释放的空间

                  代码如下:

void strongVsAutorelease1()
{
    {
        //定义强引用对象的指针
        TestClass *obj;
        @autoreleasepool
        {
            //定义自动释放类型的对象
            __autoreleasing TestClass *obj1 = [TestClass new];
            obj1.name = @"obj1";
 
            //强引用对象的指针指向自动释放类型对象的内存空间
            obj = obj1;
        }
        NSLog(@"自动释放类型的对象内存空间不会被释放,因为有strong类型的指针指向他");
    }
    NSLog(@"出上面的大括号,强类型的指针被释放,其指向的内存地址也会被释放");
}

            运行结果如下:
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类型的指针所分配的空间的情况

void strongVsAutorelease2()
{
    @autoreleasepool {
        //定义自动释放类型的对象指针
        __autoreleasing TestClass *obj;
        {
            //定义强引用类型的对象并分配内存
            TestClass *obj1 = [TestClass new];
            obj1.name = @"obj1";
            //自动释放类型的对象指针指向强引用类型内存空间
            obj = obj1;
        }
        NSLog(@"出上面的大括号,强引用类型指针指向的内存空间不会被释放,因为为还有指针指向改内存");
    }
    NSLog(@"堆分配的内存在出自动释放池的时候被释放了");
}

        代码运行结果:
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指针被置空时该内存不会被释放。

    ​    ​代码如下:

//strong 类型的指针会指向自动释放类型的空间内存,当strong指针被置空时该内存不会被释放。
void strongVsAutorelease3()
{
    @autoreleasepool {
        //定义自动释放类型的对象指针并分配内存
        __autoreleasing TestClass *obj = [TestClass new];
        obj.name = @"obj";
 
        {
            //定义强引用类型的对象
            __strong TestClass *obj1 = obj;
            //自动释放类型的对象指针指向强引用类型内存空间
            obj1 = nil;
        }
 
        NSLog(@"当obj1值空是其指向的内存空间不会被释放");
    }
    NSLog(@"当出四栋释放池的时候,该内存空间会被释放");
}

    ​代码运行结果:
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.弱类型和自动释放类型的混用

        代码如下:

//weak类型的指针指向自动释放的空间
void weakVsutorelease4()
{
    {
        //定义弱引用对象的指针
        __weak TestClass *obj;
        @autoreleasepool
        {
            //定义自动释放类型的对象
            __autoreleasing TestClass *obj1 = [TestClass new];
            obj1.name = @"obj1";
 
            //弱引用对象的指针指向自动释放类型对象的内存空间
            obj = obj1;
        }
        NSLog(@"自动释放类型的对象内存空间会被释放,因为只有weak类型的指针指向他");
        NSLog(@"%p", obj);
    }
    NSLog(@"出上面的大括号,指针已经被释放。");
}

        代码运行结果:
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类型的不算), 自动释放类型的指针如果没有其他类型的指针指向该内存空间时,当自动释放池结束后就会释放。

        上面的总结暂且这么说吧,是根据笔者自己的理解所总结的内容,不免有偏颇之处,欢迎批评指正。

相关文章
|
1月前
|
监控 算法 Java
如何确保Python的内存管理机制能够有效地工作?
【2月更文挑战第19天】【2月更文挑战第57篇】如何确保Python的内存管理机制能够有效地工作?
|
1月前
|
存储 Java Python
谈谈你对 Python 的内存管理机制的理解。
【2月更文挑战第19天】【2月更文挑战第55篇】谈谈你对 Python 的内存管理机制的理解。
|
27天前
|
存储 缓存 Java
金石原创 |【JVM盲点补漏系列】「并发编程的难题和挑战」深入理解JMM及JVM内存模型知识体系机制(1)
金石原创 |【JVM盲点补漏系列】「并发编程的难题和挑战」深入理解JMM及JVM内存模型知识体系机制(1)
37 1
|
1月前
|
监控 安全 算法
深入理解操作系统的内存管理机制
【2月更文挑战第30天】 本文旨在探讨操作系统中至关重要的一环——内存管理。与传统摘要不同,本文将直接点出核心议题:操作系统是如何通过复杂的数据结构和算法实现对计算机内存的有效管理和优化。文章将详细阐述内存管理的关键组成部分,包括内存分配、虚拟内存技术、分页和段机制等,并探讨它们如何共同协作以支持多任务处理和保护系统安全。通过对这些机制的深入了解,读者可以更好地把握操作系统设计之精髓及对现代计算环境的深远影响。
|
2天前
|
存储 算法
深入理解操作系统的内存管理机制
【4月更文挑战第24天】 在现代计算机系统中,操作系统扮演着资源管理者的角色,其中内存管理是其核心职责之一。本文将探讨操作系统如何通过内存管理提升系统性能和稳定性,包括物理内存与虚拟内存的概念、分页机制、内存分配策略以及内存交换技术。我们将透过理论与实践的结合,分析内存管理的关键技术及其对系统运行效率的影响。
|
8天前
|
存储 算法 数据安全/隐私保护
深入理解操作系统的内存管理机制
【4月更文挑战第17天】 在现代计算机系统中,操作系统扮演着资源管理者的角色,其中内存管理是其核心职能之一。本文探讨了操作系统内存管理的关键技术,包括虚拟内存、物理内存分配与回收、分页和分段机制,以及内存交换技术。通过分析这些机制的原理和实现,我们旨在加深读者对操作系统如何有效管理和保护内存资源的理解。
9 1
|
10天前
|
算法
深入理解操作系统的内存管理机制
【4月更文挑战第15天】 本文将探讨操作系统中至关重要的一环——内存管理。不同于通常对内存管理概念的浅尝辄止,我们将深入研究其核心原理与实现策略,并剖析其对系统性能和稳定性的影响。文章将详细阐述分页系统、分段技术以及它们在现代操作系统中的应用,同时比较它们的效率与复杂性。通过本文,读者将获得对操作系统内存管理深层次工作机制的洞见,以及对设计高效、稳定内存管理系统的理解。
|
21天前
|
缓存 监控 算法
深入理解操作系统的内存管理机制
【4月更文挑战第5天】 随着现代计算机系统的发展,操作系统的内存管理已成为确保系统高效稳定运行的关键因素。本文旨在探讨操作系统中内存管理的基本原理、关键技术及其在实际应用中的优化策略。通过分析内存分配、虚拟内存技术以及内存保护和分页机制等方面,揭示内存管理对提升系统性能的重要性,并提供了一系列优化内存使用效率的方法。
|
22天前
|
存储 算法 开发者
深入理解操作系统的内存管理机制
【4月更文挑战第3天】 本文旨在探讨操作系统中至关重要的一环——内存管理。不同于常规的技术分析文章,我们将从宏观和微观两个维度来剖析内存管理的核心原理及其对系统性能的影响。通过深入研究分页、分段以及虚拟内存等关键技术,我们揭示了操作系统如何优化资源分配,实现多任务并发执行的同时保证系统的稳定与高效。本文不仅适用于计算机科学专业的学者和学生,同时也为软件开发者提供了宝贵的参考,帮助他们设计出更高效的程序。
|
27天前
|
存储 算法
数据结构之动态内存管理机制(下)
数据结构之动态内存管理机制
17 1