浅谈内存管理及僵尸对象

简介: 浅谈内存管理及僵尸对象

//--------------------内存管理

内存管理范围:

管理任何继承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方法名改了

如果使用中括号调用方法,要必须把方法名写正确了

如果使用点语法,就可以.实例变量名


相关文章
|
存储 安全 算法
深入剖析JVM内存管理与对象创建原理
JVM内存管理,JVM运行时区域,直接内存,对象创建原理。
40 2
|
1月前
|
存储 算法 安全
【JVM】深入理解JVM对象内存分配方式
【JVM】深入理解JVM对象内存分配方式
29 0
|
1月前
|
Python
Python中如何判断两个对象的内存地址是否一致?
Python中如何判断两个对象的内存地址是否一致?
18 0
|
1月前
|
存储 安全 Java
【JVM】Java堆 :深入理解内存中的对象世界
【JVM】Java堆 :深入理解内存中的对象世界
53 0
|
2月前
|
存储 编译器 程序员
近4w字吐血整理!只要你认真看完【C++编程核心知识】分分钟吊打面试官(包含:内存、函数、引用、类与对象、文件操作)
近4w字吐血整理!只要你认真看完【C++编程核心知识】分分钟吊打面试官(包含:内存、函数、引用、类与对象、文件操作)
107 0
|
2月前
|
存储 缓存 算法
深入理解JVM - 对象分配内存
深入理解JVM - 对象分配内存
29 1
|
3月前
|
存储 缓存 算法
对象和数组并不是都是在堆上分配内存的
对象和数组并不是都是在堆上分配内存的
25 0
|
8月前
|
缓存 关系型数据库 MySQL
高性能内存对象缓存Memcached
高性能内存对象缓存Memcached案例
|
4月前
|
存储 Java
JVM(四):对象的内存布局
JVM(四):对象的内存布局
|
4月前
|
存储 算法 安全
Come on ! Java对象内存分配与回收策略
Come on ! Java对象内存分配与回收策略
42 0

相关实验场景

更多