------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
一.为什么要管理内存
1.移动设备的内存很有限,每个app所能占用的内存是由限制的。
2.当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存。
3.手动管理内存:
在ios5之后Xcode增加了ARC这个编译器特性,它会自动帮我们生成管理内存的代码。
因为我们以下讨论的内存管理,是在非ARC机制下的手动管理内存,所以需要关闭ARC:
Build Settings —>Apple LLVM 6.0-Language -Objective C —>Objective -C Automatic Reference Counting。选择no。
4.管理范围
1>对任何继承自NSObject的对象的内存都需要管理,根本原因是对象存放在堆空间中,堆空间的东西是动态分配的,系统不会自动回收,需要通过程序员手动控制释放对象占用的内存。
2>其他基本数据类型(in、char、float、double、struct、enum等)数据会存放在栈中。栈空间存储的东西系统会自动回收的,无需程序员管理。
二.引用计数器
1.介绍
1>每个OC对象都有自己的引用计数器,是一个整数。
2>每个对象的引用计数器用于表示该对象被引用的次数,即由多少人正在使用这个OC对象。
3>每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
4>当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器值默认是1。
2.作用
1>系统根据对象中引用计数器的数值,来判断是否回收该对象占用内存。
2>程序员通过操作对象的引用计数器,来控制对象被销毁的时机。
3.操作引用计数器
1>给对象发送一条retain消息,可以使该对象引用计数器值+1。
2>给对象发送一条release消息,可以使该对象引用计数器值-1。
3>给对象发送retainCount消息获得该对象的引用计数器值。
三.对象的销毁
1.对象何时被销毁
1>程序运行过程中,当系统检测到OC对象的引用计数器值为0时,系统就会回收对象占用的内存。
2>如果对象的计数器始终不为0,那么整个程序运行过程中,它占用的内存就不可能被回收,除非整个程序已经退出(IOS程序运行时main函数是死循环的,如果不回收对象,对象就会一直占用内存)。
2.销毁对象细节
1>当一个对象被销毁时,系统会自动向对象发送一条dealloc消息。
2>- (void)dealloc方法是继承自NSObject的对象方法,所以每个OC对象都有该方法。
3>通常我们会重写对象的dealloc方法,在这里释放相关资源,dealloc就像对象的遗言。
4>重写dealloc方法时,必须在最后面调用父类的dealloc方法([super dealloc])。
程序示例 1:
// 重写dealloc方法
- (void)dealloc
{
// 在[super dealloc]之前释放相关内存
// 也可以写一些打印提示语句
NSLog(@"某某对象被销毁了");
[super dealloc]; // 必须在最后调用父类的dealloc方法
}
四.野指针和空指针
1.僵尸对象
1>所占用内存已经被回收的对象称为僵尸对象。僵尸对象是不能再被使用。
2>开启僵尸对象检测:
默认情况下,Xcode是不会检测僵尸对象的,使用一块被释放的内存也不回报错。为了方便测试,应该开启僵尸对象检测。Edit Scheme —>Diagnostics—>Enable Zombie Objects
3>经典错误: message sent to deallocated instance 给已经释放的对象发送消息。
程序示例 2:
int main()
{
Person *p = [[Person alloc] init];
[p release]; // 对象p内存被回收
p.name = @"jack"; // 程序运行到这里会报错: message sent to deallocated instance
NSLog(@"%@", p.name);
return 0;
}
2.野指针
1>野指针:(OC中是指)指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错。
2>野指针错误:
当使用野指针操作一块不可用内存时,运行时会发出错误:EXC_BAD_ACCESS。意思为访问了一块不可用的内存(已经被回收)。我们称这个经典错误为野指针错误。
3.空指针
1>没有指向任何东西的指针(存储的东西是nil、NULL、0)。
2>OC中不存在空指针错误,给空指针发送消息不会报错,与Java不同。
3>可以通过清空指针,来防止野指针错误。
程序示例 3:
int main()
{
Person *p = [[Person alloc] init];
[p release]; // 对象p内存被回收
p = nil; // 通过清空指针,可以防止野指针错误
p.name = @"jack"; // 向空指针发送消息,相当于什么都没有做。程序不会报错
NSLog(@"%@", p.name);
return 0;
}
五.管理内存遵循的原则
1.当我们手动管理内存时,需要遵循一些原则,这些原则使我们管理内存时逻辑更加清晰,不容易出错。
2..管理原则
我们需要明确责任人,也就是明确谁使用了对象(使用者)。使用者负责对对象的引用计数器进行操作。
1>谁创建,谁release
如果你通过alloc、new或copy方法来创建一个对象,那么当你不需要该对象时必须向对象发送一条release消息或autorelease消息(之后会详细介绍)。
程序示例 4:
int main()
{
Person *p = [[Person alloc] init]; // 程序通过alloc方法创建1个Person对象p
Person *p1 = [Person new]; // 程序通过new方法创建1个Person对象p1
p.name = @"jack"; // 为p的name属性赋值
NSLog(@"%@", p.name); // 打印p的name属性
[p release]; // 当我们不再使用对象了,要对p做1次release操作。总之要有一个release与之前的alloc对应否则就会造成内存泄露
p = nil; // 再严谨一些,我们可以清空指针,防止野指针错误
[p1 release]; // 也同样要对p1做一次release操作
p1 = nil;
return 0;
}
2>谁要用,谁retain —>谁retain,谁release (例外循环retain)
谁想引用某个对象,谁就向对象发送一条retain消息(引用计数器+1)。谁不再用某个对象时,谁就向该对象发送一条release消息(引用计数器-1)。
3>只要还有人用某个对象,那么这个对象就不会被回收。
4>有始有终,有加必有减。
当你对一个对象进行了一次retain操作,必然还要对该对象进行一次release操作。