堆和栈
栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈(先进后出);
堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由系统回收,分配方式类似于链表。
内存管理
所谓内存管理, 就是对内存进行管理, 涉及的操作有:
分配内存 : 比如创建一个对象, 会增加内存占用
清除内存 : 比如销毁一个对象, 能减小内存占用
内存管理的本质
OC对象存放于堆里面
非OC对象一般放在栈里面(栈内存会被系统自动回收)
引用计数器和dealloc基本概念
什么是引用计数器
每个OC对象都有自己的引用计数器,它是一个整数,表示有多少人正在用这个对象
引用计数器的作用
当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1
当对象的引用计数器为0时,对象占用的内存就会被系统回收
如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出 )
操作引用计数器
给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身
给对象发送一条release消息, 可以使引用计数器值-1
给对象发送retainCount消息, 可以获得当前的引用计数器值
需要注意的是: release并不代表销毁\回收对象, 仅仅是计数器-1
dealloc 方法的作用
对象即将被销毁时系统会自动给对象发送一条dealloc消息
(因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)
单个对象内存管理
ARC和MRC
ARC: Automatic(自动) Reference(引用) Counting(计数)
自动引用计数
不需要程序员管理内容,编译器会在适当的地方自动给我们添加release/retain等代码
注意点: OC中的ARC和java中的垃圾回收机制不太一样, java中的垃圾回收是系统干得, 而OC中的ARC是编译器干得
MRC: Manul(手动) Reference(引用) Counting(计数)
什么是手动引用计数?
所有对象的内容都需要我们手动管理, 需要程序员自己编写release/retain等代码
重写dealloc方法有什么注意点
重写dealloc方法, [super dealloc]一定要写到所有代码的最后
多对象内存管理
内存管理的原则
1.谁创建谁release
如果你通过alloc、new、copy或mutableCopy来创建一个对象,那么你必须调用release或autorelease
2.谁retain谁release:
只要你调用了retain,就必须调用一次release
3.总结:
有加就有减,曾经让对象的计数器+1,就必须在最后让对象的计数器-1
set方法内存管理
"理解set方法内存管理的推导过程,推导过程不需要记,只需要记忆结论就可以"
实现set方法内存管理有哪几步
(1)retain需要使用的对象
(2)release之前的对象
(3)只有传入的对象和之前的不同才需要release和retain
- (void)setRoom:(Room *)room
{
// 1.只有房间不同才需用release和retain
if (_room != room) {// 0ffe1 != 0ffe1
// 2.将以前的房间释放掉 -1
[_room release];
/*
// 3.对房间的引用计数器+1
[room retain];
//4.将新房间赋值给实例变量
_room = room;
*/
// retain不仅仅会对引用计数器+1, 而且还会返回当前对象
_room = [room retain];
}
}
dealloc中release对象可以如何简化
- (void)dealloc
{
NSLog(@"%s", __func__);
/*
[_text release];
_text = nil;
*/
// 下面这句话相当于调用了set方法
// 先release旧值, 然后再将新值赋值给属性
self.text = nil;
[super dealloc];
}
野指针和空指针
什么是僵尸对象?什么是野指针?什么是空指针?如何避免野指针错误?
1.僵尸对象已经被销毁的对象(不能再使用的对象)
2.野指针指向僵尸对象(不可用内存)的指针给野指针发消息会报EXC_BAD_ACCESS错误
3.空指针没有指向存储空间的指针(里面存的是nil, 也就是0) 给空指针发消息是没有任何反应的
4.为了避免野指针错误的常见办法: 在对象被销毁之后, 将指向对象的指针变为空指针