//--------------------内存管理
内存管理范围:
管理任何继承NSObject的对象,基本数据类型不需要内存管理。
对象类型是程序运行过程中动态分配的,存储在堆区,内存管理主要是对"堆区中的对象"进行内存管理。
//--------------------概念
1)对象的所有权及引用计数
任何对象都可能拥有一个或多个所有者。只要一个对象至少还拥有一个所有者,它就会继续存在
2)对象的引用计数器
每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。
在每个OC对象内部,都专门有8个字节的存储空间来存储引用计数器。
3)引用计数器的作用
判断对象要不要回收的唯一依据
(存在一种例外:对象值为nil时,引用计数为0,但不回收空间)就是计数器是否为0,若不为0则存在。
//----------------------对引用计数器的操作3个方法
给对象发送消息,进行相应的计数器操作。
retain消息:使计数器+1
release消息:使计数器-1(并不代表释放对象)
retainCount消息:获得对象当前的引用计数器值rc
//--------------------------对象的销毁
当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。
//1.对象销毁就自动执行dealloc方法
当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的"临终遗言"。
//2.重写dealloc方法
一旦重写了dealloc方法就必须调用[superdealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)。
//3.对象销毁了存储空间不可用
一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)。
#注意:
1)如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出)
2)任何一个对象,刚生下来的时候,引用计数器都为1。(对象一旦创建好,默认引用计数器就是1 )
3)当使用"alloc"、"new"或者"copy"("mutablecopy")创建一个对象时,对象的引用计数器默认就是1 ;
对应的方法:
-retain +1
-release -1
-retainCount当前引用技数为多少0 --->系统释放这个对象
-dealloc
#warning开放如何使用:需要理解MRC,但实际使用时尽量用ARC
//------------------------MRC
target Build Settings Basic Leveis搜索auto-->NO
内存管理的关键是"如何判断对象被回收"
问题:
1.对象创建完之后默认引用计数是多少? 1
2.当对象的引用计数为多少? 0系统就会回收这个对象的空间
3.怎么知道这个对象空间被释放了?
重写dealloc方法在dealloc方法中打印以下信息
-------------------关于dealloc方法
代码规范:
(1)一定要[superdealloc],而且要放到最后,意义是:"先释放子类占用的空间再释放父类占用的空间"
(2)对self(当前)所拥有的的其他对象做一次release操作
-(void)dealloc
{
//先释放子类拥有的对象属性
[superdealloc];
}
注意:
永远不要直接通过对象调用dealloc方法(对象的引用计数为0系统会自动调用此方法)
-------------------------------------
原则
1.只要还有人在使用某个对象,那么这个对象就不会被回收;
2.只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;
3.当你不想使用这个对象时,应该让对象的引用计数器-1;
记住两点:
1,谁创建"alloc","new",谁"release";
2,谁"retain",谁"release";
正确的手动管理操作:
一个alloc/new对应一个release
一个retain对应一个release
总结
有始有终,有加就应该有减,曾经让某个对象计数器加1,就应该让其在最后减1;
//---------------------僵尸对象与内存泄露
区分以下2个概念:
1)"僵尸对象"(野指针)(没有初始化的指针变量;指向的内存空间已经被释放的指针变量)
栈区,系统会自动管理
//空指针:没有指向任何东西的指针,给空指针发送消息不会报错对象= nil
2)"内存泄露"(栈区的指向已经释放,堆区的空间没有释放,这时堆区的空间就被泄露了)
堆区,需要程序员手动管理
#不管是多个对象还是单个对象,只要我们研究它的内存管理,就两点:
#1.僵尸对象(野指针)
#2.内存泄露
//---------------------------关于僵尸对象问题
首先什么是僵尸对象:
"僵尸对象"(野指针)(没有初始化的指针变量;指向的内存空间已经被释放的指针变量)
栈区,系统会自动管理
#检测野指针
(检测僵尸对象,比较耗费性能,所以Xcode默认是不检测的)
打开方法:
Edit Scheme --> Run Debug ---> Diagnostics --> Enable Zombie Objects(打勾)
对象一旦成为了僵尸对象,就不能再使用这个僵尸对象了
#判断是否是僵尸对象:
person(对象) -->看什么?
看是否执行了dealloc方法--->重写dealloc方法打印一句话提醒
dealloc方法一旦调用了,意味着堆区的空间已经被释放,释放就意味着这个对象是僵尸对象
#避免使用僵尸对象的方法
1)僵尸对象调用方法,会报错,访问成员变量有时是可以的(但是这个是未知的)。
2)为了防止不小心调用了僵尸对象,可以将对象赋值nil(对象的空值)
用nil调用方法是不会报错的。
#关键:
3)总而言之要合理使用release和retain
//-----------------nil和Nil及NULL、NSNull的区别:
了解:
1.nil对象的值person =nil;
2.Nil类对象的值
3.NULLC语言的关键字通用空指针
4. NSNull空对象用在不能使用nil的地方
//---------------------------关于内存泄露问题
首先:
什么是内存泄露:
"内存泄露"(栈区的指向已经释放,堆区的空间没有释放,这时堆区的空间就被泄露了)
堆区,需要程序员手动管理
#对象的内存泄露的几种情况
1) retain和release不匹配,retain多余release导致的内存泄露;
2)对象使用过程中,没有被release,而被赋值为nil;
3)在方法中不当的使用了retain;
#关键:
3)总而言之要合理使用release和retain
还是记住两点:
1.谁创建"alloc","new",谁"release";
2.谁"retain",谁"release";
内存管理的验证:
1.查看retainCount的值
2.重写dealloc方法,查看对象是否调用了dealloc方法
/*
一个alloc/new对应一个release
一个retain对应一个release
*/
//-----------------------多对象的内存管理
#多个对象的僵尸对象问题
假设对象A与对象B有(关联)关系,如果对象B独自销毁,会影响对象A的操作.
{
Car *_car;
}
假设:person拥有车
{
//创建一个人对象rc = 1
Person *person = [Person new];
//创建一个车对象rc = 1
Car *car = [Car new];
//让人拥有这辆车
person.car = car;
//人调用开车这个方法
[person driver];
[car release];
#关键的地方在这里看这里!!
# [person driver]不能再调用这个方法人是拥有车的,人都还没释放,就不能调用与car有关系的driver方法是不科学的
[person release];
}
#解决方案
1.在人的实例变量_car的set方法中set方法中[_car retain]; +1
2.在人的dealloc方法中先把自己拥有的对象先释放[_car release]; -1
总结:
凡事关联关系,对象A与对象B有(关联)关系,就应该在set方法中让关联的对象+1,然后在对象A的dealloc方法中让关联的对象-1.
//------------------------------------------------
#多个对象的内存泄露问题
问题:
就上面的解决方法,还是有弊端,会造成原对象泄露
比如说人中途换车了多次调用了关于_car的set方法导致_car多次retain而没有相对应的release方法,导致rc没变为0,对象没有释放,就说内存泄露了
1.人第"1"次使用set方法赋值第一次把byd赋值给人的_car
2.人第"2"次使用set方法赋值第二次把byd赋值给人的_car
3.人第"3"次使用set方法赋值第三次把bigben赋值给人的_car
1.byd(首次赋值) _car =nil_car != bydif执行[_car release]由于_car是nil[nilrelease]赋值赋值之后_car retain byd+1
2.byd(多次调用) _car = byd _car == bydif不执行
3.bigBen(换车) _car = byd car = bigBen _car != carif执行[_car release] --> [byd release]赋值赋值指针bigBen retain bigBen + 1
在dealloc方法中:
如果只执行了第1步或者第1第2步则:
[_car release]; _car (byd) release -1
如果执行了第1,2,3步则:
[_car release];// _car ---> bigBen -1
-(void)setCar:(Car *)car{
if(_car != car){// _car != car _car实例变量!= car参数
[_car release];//先release旧值
_car = car;//赋新值_car = [car retain];
[_car retain];//再retain新值
}
#判断是否是同一个对象,release旧值,retain新值
}
-(void)dealloc{
//该对象在被释放之前,必须先把自己所拥有的对象释放,也就是release一次自己的实例变量(对象)
[_car release];
[superdealloc];
}
//----------------------------set方法内存管理
#基本数据类型: int float double long struct enum基本数据类型做实例变量,就正常写法
-(void)setAge:(int)age{
_age = age;
}
#对象类型:对于对象作为另一个类的实例变量
判断是否是同一个对象,release旧值,retain新值
-(void)setCar:(Car *)car{
if(_car != car){
[_car release];//先release旧值
_car = car;//赋新值
[_car retain];//再retain新值
}
}
//------------------------@property参数
@property帮我们生成get和set方法的声明和实现
#格式: @property (参数1,参数2)数据类型实例变量名(记住:此处不要带下划线)
#原子性: (习惯不加锁)
"atomic":默认值,对属性加锁,多线程下线程安全
"nonatomic":对属性不加锁,多线程下不安全,但速度快
#读写属性: (用得不多)
"readwrite":默认值,生成getter,setter方法.
"readonly":只生成getter方法
# setter方法的处理
1)基本数据类型或者C语言的构造类型:直接赋值
intfloatdoublelongstructenum等BOOL/ Boolean
"assign":@property的默认值,直接赋值
//如果使用assign set方法就会生成如下代码
-(void)setAge:(int)age{
_age = age;
}
2)OC对象类型
"retain":先release原来的值,再retain新值
"copy":先release原来的值,再copy新值(NSString *)
//如果使用retain set方法就会生成如下代码
-(void)setCar:(Car *)car{
if(_car != car){
[_car release];
_car = [car retain];
}
}
练习
{
Car *_car;
int_age;
enumSex _sex;
Dog *_dog;
NSString *_name;
}
@property(retain,nonatomic) Car *car;
@property(nonatomic,assign)intage;
@property(nonatomic,assign)enumSex sex;
@property(nonatomic,retain) Dog *dog;
@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign,setter= setIsVip:,getter= haha)BOOLvip;
原方法名
-(void)setVip:(BOOL)vip;
-(BOOL)vip;
setter = setIsVip:,getter = haha
// setter=setIsVip:
// getter=haha方法名改了
如果使用中括号调用方法,要必须把方法名写正确了
如果使用点语法,就可以.实例变量名