iOS的内存管理机制

Posted by DH on August 3, 2017

概述

在iOS开发中主要有量类型的对象,一种是值类型(int、float、strict等基本数据类型),一种就是OC对象,其中值类型是存储在栈上,操作系统会自己管理, OC对象类型存在堆上,是需要我们进行管理的。

iOS的内存管理也就是引用计数。

每一个OC对象内部都有一个整数(4个字节)来存储该对象被引用的次数,称之为引用计数器。如果引用计数器是0,该对象被回收,如果不是0,不回收。总的指导思想就 是这个。

那么引用计数器具体是如何变化的呢?

(1)如果一段代码需要一个对象,这个对象的引用计数就+1;

(2)一段代码使用该对象后,不需要这个对象,这个对象的引用计数就-!;

(3)当引用计数为0,就释放这个对象。

对应的具体操作是:

(1)给对象发retain消息,对象引用计数+1;

(2)给对象发送release消息,对象引用计数-!;

(3)引用计数为0,调用对象的dealloc函数,销毁对象。

管理原则

“谁创建,谁释放,谁引用,谁管理”。具体来说,就是你通过alloc,copy,new,mutableCopy生成并持有对象,通过retain持有对象,在使用完对象之后, 都必须向对象发送release消息。

当一个对象的引用计数为0的时候,就销毁该对象。

自动释放池

在iOS开发中,我们会遇到很多对象只用一次就不需要了,如果我们每次都去手动的释放会很累,iOS提供了一种机制,将这些对象放到一个池子中,在适当的时候, 一起释放这些对象,这里的释放是给池子中的对象发送一个release消息,是池子中的对象的引用计数减小1.

但是,并不是所有的池子中的对象都会被释放,如果池子中的对象引用计数>1,该对象依然不会被释放。

当一个对象被放入到自动释放池,这个对象不会马上被释放,还可以继续使用,在未来某个时间,自动释放池对象收到release消息,才会销毁。

使用方法:

// 方式一
@autoreleasepool {
        
}		


// 方式儿
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
//这里写代码
[pool release];

一般使用方式二,效率更高。

自动释放池的生成有两种方式: (1)手动生成,就像上面的代码;

(2)系统在程序事件处理之前自动创建

自动释放池的销毁有两种方式: (1)手动销毁,调用release;

(2)系统在程序时间处理之后自动释放

使用自动释放池需要注意三个问题:

(1)当池子中某些对象的引用计数>1时,对象无法销毁;

(2)自动释放池会在某一时间集中释放,如果操作需要生成的对象较多占用内存空间大,可以使用多个释放池来进行优化。比如在一个循环中需要创建大量的临时变量, 可以创建内部的池子来降低内存占用峰值

例如:

while(i < 10000)
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"ouput:%d",i];
        NSLog(@"%@",str);
        i++;
    }	

(3)autorelease不会改变对象的引用计数

MRC内存管理方式

MRC手动管理内存,就要严格按照“谁创建,谁释放,谁引用,谁管理”的原则。

这里的创建主要是通过alloc,new,copy,mutalblecopy,创建对象,这个时候新建对象的引用计数是1.

引用就是指,你给某个对象发送一个retain消息,对象的引用计数就+1.

使用完毕后,要给对象发送release消息。

同时需要注意的是野指针(悬垂指针),在释放对象的时候,要将指针赋值为nil。

野指针是指该指针指向的内存被释放是收回。

内存泄漏,是指程序没有将不再使用的内存释放,简单说就是没有指针指向某一个未被释放的内存。

另外,需要注意如果一个对象不是用alloc,new,copy,mutalblecopy,创建对象,而是用便利构造器创建的临时对象,这个对象会被立即加入到内部的 自动释放池中,不需要关心何时销毁。

例如:NSMutableArray *arr = [NSMutableArray array];

ARC内存管理方式

ARC相当于系统给我们完成了我们应该去做的事,自动在适当的地方加入release/retain等,在iOS5推出。

ARC修饰符

一共四中修饰符:

__strong :强引用,默认的对象修饰符,持有对象所有权。自动将对象初始化为nil。

__weak:弱引用,不持有对象所有权,主要用于解决循环引用,引用指向的对象释放之后,直接被置为nil,避免悬垂指针。

__unsafe_unretained:主要用于Mac,iOS开发基本用不到。主要兼容iOS5以下的版本。现在基本不用。

__autoreleasing:对象前面有__autoreleasing修饰时,相当于在ARC无效时,调用对象的autoreleasing方法,将对象注册到自动释放池。

方法内部创建的变量,在方法外部使用,要添加__autoreleasing。

但是显示的附加__autoreleasing很少见,通常是隐式的调用,在取得非自己创建并持有的对象时,编译器会检查方法是不是alloc,new,copy,mutalblecopy创建的, 如果不是,就会自动的将返回值的对象注册到autoreleasepool.

例如:id obj = [NSMutableArray array];

总的来说,ARC的strong相当于MRC的retain,weak相当于MRC的release。