内存管理
概念: 由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,个app可用的内存是被限制的,如果一个app使用的内存超过20M,则系统会向该app发送Memory Warning消息。收到此消息后,需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量,否则程序会崩溃。
OC内存管理的范围:管理任何继承NSObject的对象,对其他的非OC基本数据类型无效。
本质原因: 因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
Objective-C提供了三种内存管理方式:
1.MannulReference Counting(MRC,手动管理,在开发 iOS4.1之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain、release、autorelease 等,而在其后的版本可以使用 ARC,让系统自己管理内存。)
2.automatic reference counting(ARC,自动引用计数,iOS4.1 之后推出的)
3.garbage collection(垃圾回收)。iOS不支持垃圾回收;
开发中如何使用: 需要理解MRC,但实际使用时尽量用ARC
从上图我们可以看出ARC开发程序的代码会比MRC少一大部分。
内存管理中的MRC:
黄金法则:The basic rule to apply is everything that increases the reference counter with
alloc,[mutable]copy,[withZone]or retain is in charge of the corresponding [auto]release.
翻译: 如果对一个对象使用了alloc、[mutable]copy、retain,那么你必须使用相应的release或者autorelease。
对引用计数器的操作:
1.判断对象要不要回收的唯一依据(存在一种例外:对象值为nil时,引用计数为0,但不回收空间)就是计数器是否为0,若不为0则存在。
给对象发送消息,进行相应的计数器操作。
2.创建对象时:使计数器+1
3.retain消息:使计数器+1,该方法返回对象本身
4.release消息:使计数器-1(并不代表释放对象)
5.retainCount消息:获得对象当前的引用计数器值 (%ld %tu)
//main.m
Person *p = [[Person allco] init];
[p retain]; //retaincount = 2
[p release]; //retaincount = 1
[p retain]; //retaincount = 2
[p retain]; //retaincount = 3
[p release]; //retaincount = 2
[p release]; //retaincount = 1
[p release]; //retaincount = 0
内存管理的关键如何判断对象被回收 ?
(1)一定要[super dealloc],而且要放到最后,意义是:先释放子类占用的空间在释放父类占用的空间
(2)对self(当前)所拥有的的其他对象做一次release操作
-(void)dealloc
{
<span style="white-space:pre"> </span>NSLog(@"对象被销毁了");
<span style="white-space:pre"> </span>[_car release];
<span style="white-space:pre"> </span>[super dealloc];
}
注意:永远不要直接通过对象调用dealloc方法(实际上调用并不会出错)
一旦对象被回收了, 它占用的内存就不再可用, 坚持使用会导致程序崩溃(野指针错误)为了防止调用出错,可以将“野指针”指向nil(0)。p = nil;如果对象内存被回收了,就会调用重写父类的dealloc方法
内存管理的原则:
1)谁创建,谁release
(1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法
(2)不是你创建的就不用你去负责
2)谁retain,谁release;只要你调用了retain,无论这个对象时如何生成的,你都要调用release
3)有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1
-(void)setCar:(Car *)car
{
<span style="white-space:pre"> </span>if(car!=_car) //先判断传入的是否跟_car相等
<span style="white-space:pre"> </span>{//2 对旧对象做一次release
<span style="white-space:pre"> </span>[_car release];//若没有旧对象,则没有影响
<span style="white-space:pre"> </span>_car=[car retain]; //3.对新对象做一次retain</span>
<span style="white-space:pre"> </span>}
}
野指针错误:访问了一块坏的内存(已经被回收的,不可用的内存)。
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。( 默认情况下xcode为了提高编码效率,不会时时检查僵尸对象, 打开僵尸对象检测),当对象的retaincount为0时,p = nil;
关于nil和Nil及NULL的区别:
nil: A null pointer to an Objective-C object. (指向对象的空指针)
Nil: A null pointer to an Objective-C class.(指向类的空指针)
NULL: A null pointer to anything else. ( #define NULL ((void *)0) )
NULL是一个通用指针(泛型指针)。
内存管理的ARC
ARC 下不在使用retain,release,autorelease ,dealloc这些关键字,取而代之是strong、weak指针。ARC所做的只不过是在代码编译时为你自动在合适的位置插入release或autorelease,
ARC的判断准则:
只要没有强指针指向对象,对象就会被释放。
注意:当使用ARC的时候,暂时忘记“引用计数器”,因为判断标准变了。
//此时刚被初始化就会被释放掉,因为没有强指针指向当前内存
__weak Person *p = [[Person alloc] init];
//强指针指向一块区域
__strong Person *p2 = [[Person alloc] init];
p2 = nil; //此时p2所指的内存区域被释放
//p3为强指针,默认的
Person *p3 = [[Person alloc] init];
ARC下循环引用问题:
//Person.h
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
//dog是strong 强指针
@property (nonatomic,strong) Dog *dog;
@end
//Dog.h
#import <Foundation/Foundation.h>
@class Person;
@interface Dog : NSObject
//狗的主人 也是strong 强指针
@property (nonatomic,strong) Person *owner;
@end
//main.m
Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init];
p.dog = d;
d.owner = p; //此时p和d陷入了互相调用的循环中
//Person.h
@property (nonatomic,weak) Dog *dog;
或者是
//Dog.h
@property (nonatomic,weak) Person *owner;