iOS内存管理机制

  • 内存管理机制的原理是引用计数

一. Objective-C提供了两种内存管理方式:
  1. MRC,手动引用计数器(manual reference counting)

  2. ARC,自动引用计数(automatic reference counting)

  3. 引用计数简单来说就是统计一块内存的所有权,当这块内存被创建出来的时候,它的引用计数从0增加到1,表示有一个对象或指针持有这块内存,拥有这块内存的所有权,如果这时候有另外一个对象或指针指向这块内存,那么为了表示这个后来的对象或指针对这块内存的所有权,引用计数加1变为2,之后若有一个对象或指针不再指向这块内存时,引用计数减1,表示这个对象或指针不再拥有这块内存的所有权,当一块内存的引用计数变为0,表示没有任何对象或指针持有这块内存,系统便会立刻释放掉这块内存。

  4. 其中在开发时引用计数又分为ARC(自动内存管理)和MRC(手动内存管理)。ARC的本质其实就是MRC,只不过是系统帮助开发者管理已创建的对象或内存空间,自动在系统认为合适的时间和地点释放掉已经失去作用的内存空间,原理是一样的。虽然ARC操作起来很方便,不但减少了代码量,而且降低了内存出错的概率,但因为ARC不一定会及时释放,所以程序有时候可能会占用内存较大。而MRC若做得好,通过手动管理,及时释放掉不需要的内存空间,便可保证程序长时间运行在良好状态上。

二. 在MRC中会引起引用计数变化的关键字有:alloc,retain,copy,release,autorelease。(strong关键字只用于ARC,作用等同于retain)

当对象被创建(alloc、new或copy等方法)时,其引用计数初始值为1;
给对象发送retain消息,其引用计数加1;
给对象发送release消息,其引用计数减1;
当对象引用计数归0时,ObjC给对象发送dealloc消息销毁对象。

  • alloc:当一个类的对象创建,需要开辟内存空间的时候,会使用alloc,alloc是一个类方法,只能用类调用,它的作用是开辟一块新的内存空间,并使这块内存的引用计数从0增加到1,注意,是新的内存空间,每次用类alloc出来的都是一块新的内存空间,与上一次alloc出来的内存空间没有必然联系,而且上一次alloc出来的内存空间仍然存在,不会被释放。

  • retain:retain是一个实例方法,只能由对象调用,它的作用是使这个对象的内存空间的引用计数加1,并不会新开辟一块内存空间,通常于赋值是调用,如:
    对象2=[对象1 retain];表示对象2同样拥有这块内存的所有权。若只是简单地赋值,如:对象2=对象1;那么当对象1的内存空间被释放的时候,对象2便会成为野指针,再对对象2进行操作便会造成内存错误。

  • copy:copy同样是一个实例方法,只能由对象调用,返回一个新的对象,它的作用是复制一个对象到一块新的内存空间上,旧内存空间的引用计数不会变化,新的内存空间的引用计数从0增加到1,也就是说,虽然内容一样,但实质上是两块内存,相当于克隆,一个变成两个。其中copy又分为浅拷贝、深拷贝和真正的深拷贝,浅拷贝只是拷贝地址与retain等同;深拷贝是拷贝内容,会新开辟新内存,与retain不一样;真正的深拷贝是对于容器类来说的,如数组类、字典类和集合类(包括可变和不可变),假设有一个数组类对象,普通的深拷贝会开辟一块新内存存放这个对象,但这个数组对象里面的各个元素的地址却没有改变也就是说数组元素只是进行了retain或者浅拷贝而已,并没有创建新的内存空间,而真正的深拷贝,不但数组对象本身进行了深拷贝,连数组元素都进行了深拷贝,即为各个数组元素开辟了新的内存空间。

  • release:release是一个实例方法,同样只能由对象调用,它的作用是使对象的内存空间的引用计数减1,若引用计数变为0则系统会立刻释放掉这块内存。如果引用计数为0的基础上再调用release,便会造成过度释放,使内存崩溃;

  • autorelease:autorelease是一个实例方法,同样只能由对象调用,它的作用于release类似,但不是立刻减1,相当于一个延迟的release,通常用于方法返回值的释放,如便利构造器。autorelease会在程序走出自动释放池时执行,通常系统会自动生成自动释放池(即使是MRC下),也可以自己设定自动释放池,如:
    @autoreleasepool{
    obj= [[NSObject alloc]init];
    [obj autorelease];
    }
    当程序走出“}”时obj的引用计数就会减1.

三. 自动释放池
  • Autoreleasepool的原理
    自动释放池,系统有一个现成的自动内存管理池,它会随着每一个mainRunloop的结束而释放其中的对像;
    自动释放池也可以手动创建,它可以让pool中的对象在执行完代码后马上被释放,可以起到优化内存,防止内存溢出的效果(如视频针图片的切换时、创建大量临时对象时等)
    autorelease:自动释放,使对象在超出指定的生存范围时能够自动并正确地释放 (release 即是立即释放)

如:循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏。因此,在循环中创建自己的autoReleasePool,及时释放占用内存大的临时变量,减少内存占用峰值

for (int i = 0; i < 10000; i ++) {
  @autoreleasepool {
    Person* person = [[Person alloc] init];
    [person eat];          
  }
}
  • 从mrc到arc的转变
    项目 -> Build Phases -> Compile Sources 找到要修改的文件
    如果是ARC工程添加MRC文件则输入:-fno-objc-arc
    如果是MRC工程添加ARC文件则输入:-fobjc-arc
四. block内存管理

block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。

1. @property(copy, nonatomic) void(^block)(void);
mrc中 copy会把block从栈上移动到堆上。

2. @property(strong, nonatomic) void(^block)(void);
arc即时由强引用strong将其从栈复制到堆

block在一开始是处在stack上的,这是为了考虑到效率的原因,但是,有时候是需要block的生命周期长于一开始的stack,这时,我们就通过copy block 来将block复制到heap。

arc中默认的对象声明都是strong性质的,在两个或两个以上的类相互引用时,会导致循环引用,其中一方需要用weak修饰,才不会造成retainCycle,如:delegate 属性用weak声明;mrc中即用assign修饰 。
在block中引用block所属的类、实例变量或类的属性也会导致循环引用

self.block = ^{
    [self doSomething];
 };

arc中用__weak修饰self、mrc中用__block修饰,如下代码:

__weak ViewController* weakSelf = self;//arc
//__block ViewController* weakSelf = self;//mrc
self.block = ^{
   [weakSelf doSomething];
};

你可能感兴趣的:(iOS内存管理机制)