------<a href="http://www.itheima.com"target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流
MRC内存管理
MRC内存管理:我认为其实就是手动管理堆区的内存空间。
对象在堆区的空间:
<span style="font-size:14px;">#import<Foundation/foundation.h>
#import "Dog.h"
int main(){
@autoreleasepool{
//创建一个对象
//对象创建完成以后,默认的所有者有一个,所以引用计数为一
Dog *byb=[Dog new];//1
[byd eat];
NSLog(@"byd.retainCount=%lu",byd.retainCount);
//如果一个对象已经被释放了,这个对象就称位僵尸对象
[byd release];//0
//这句话默认不报错,开启僵尸检查才报错
[byd eat];//这就是使用僵尸对象调用方法,这个时候byd也就称为野指针
//野指针其实就是被释放了空间的指针
//对象只要变成僵尸对象就不能用了,必须重新申请才能用
}
}</span>
避免使用僵尸对象 :
<pre name="code" class="objc"><span style="font-size:14px;">#import<Foundation/foundation.h>
#import "Dog.h"
int main(){
@autoreleasepool{
Dog *d=[[Dog alloc] init];//1
[d release];//0
//
//[d retain]想让d起死回生,开启僵尸对象检测的话会报错
避免使用僵尸对象的方法是,对象释放后了以后,给对象赋值为nil
d=nil;
[d retain]//僵尸对象使用不会报错了(开启僵尸检测的情况下)
}
}</span>
单个对象的内存泄露问题
<span style="font-size:14px;">#import<Foundation/foundation.h>
#import "Dog.h"
int main(){
@autoreleasepool{
//内存泄露情况
//创建完成使用后没有release
Dog *d=[[Dog alloc] init];//}
//retain完之后没有release
[d retain];//2
[d release];//1
//不当的使用nil
Dog *d=[[Dog alloc] init];
d=nil;
[d eat];
[d release];
}
return 0;
}
</span>
引用计数器:
用来保存当前对象有几个东西在使用它(数字)
引用计数器的作用:
用来判断对象是否应该回收内存空间(如果对象不等于nil,当引用
计数器为0,此时要回收对象的内存空间)
引用计数器的操作:
retain 使得引用计数器+1
release 使得引用计数器-1
retainCount 得到引用计数器的值
如果一个对象被释放的时候,会有“临终遗言”(会调用该对象的dealloc方法)
注意:
dealloc方法是NSObject的,一般我们要重写dealloc方法
在dealloc方法的内部,要调用[super dealloc];
手动内存管理:
MRC手动内存管理
ARC自动内存管理
注意:
我们创建一个项目,此时默认的是ARC(自动内存管理)
把项目从ARC模式改成MRC模式
dealloc方法是对象的临终遗言的方法
对象被撤销的时候,会默认的调用该方法(此方法可以重写,但是不要在main函数中出现)
内存管理:
对象如果不在使用了,就应该回收它的空间防止造成内存泄露。
内存管理的范围:
所有的集成了NSObject的对象的内存管理
基本数据类型(C语言中的数据类型就称为基本数据类型)的数据内存
不需要我们管理
内存管理的原则:
一、
如果对象有人使用,就不应该收回
如果你想使用这个对象,应该让这个对象retain一次
如果不想使用这个对象,应该让这个对象relase一次
二、
谁创建 谁release
三、
谁retain 谁release
内存管理研究的内容:
一、野指针
定义的指针变量没有初始化
指向的空间已经被释放了
二、内存泄露
{
Person *p=[Person new];
}
p栈区
[Person new]; 堆区
如果栈区的p已经释放了,而堆区的空间还没有释放,堆区的空间就被泄露了
set方法内存管理
set方法的写法:
判断_car存放的释放是形参对象,如果不是,则执行[_car release];
<span style="font-size:14px;">if(_car!=car){
[_car release];//先释放上一个对象,(注意第一次是向nil发送release消息)
_car=[car retain];
}
}</span>
上诉判断是一个固定写法!
基本数据类型的数据作为实例变量
int _speed;
set方法的写法
<span style="font-size:14px;">-(void)setSpeed:(int)speed{
_speed=speed;
}
Dog* _dog;
//对于对象作为另外一个类的实例变量
-(void)setDog:(Dog*)dog{
//判断对象是否是原来的对象
if(_dog!=dog){
//release旧值
[_dog release];
//retain 新的值并且赋值给实例变量
_dog=[dog retain];
}
}</span>
@porperty参数
格式:@porperty(参数1,参数2) 数据类型 方法名
参数类别 参数 说明
原子性 atomic 对属性枷锁,多线程下线程安全,默认值
nonatomic 对属性不枷锁,多线程下不安全,但是速度快
读写属性 readwrete 生成getter、setter,默认值
readonly 只生成getter方法
set方法处理 assign 直接赋值,默认值
retain 先release原来的值,再retain新值
copy 先release原来的值,再copy新值
先看set方法中:
什么时候使用retain(实用于OC对象)
在一个类中有关联其他对象的时候,这个对象的@property(nonatomic,retain)
什么时候使用assign
实例变量是剧本数据类型的时候(实用与非OC对象)
set和get名称:
<span style="font-size:14px;">@property(nonatomic,assig,setter=isVip:)BOOL Vip;
Person *p=[Person new];
p.vip=YES;//替换set方法@property(nonatomic ,setter=isVip:) [p isVip:YES];
if(p.vip){
NSLog(@"是Vip");
}</span>
@class的使用
使用格式:
@class 类名;
@class xxx;
含义:告诉编译器,xxx是一个类,至于类有哪些属性和方法,此处不去检查
好处:如果xxx文件内容发生了改变,而不需要重新编译
@class的使用注意:
1、.h @class xx
2、.m #import "xx.h"
@class的特殊用法:可以解决循环引入问题(比如A中用import引入B,B中
用import引入A时会报错,这样就可以用@class解决问题,就不会报错)。
在.h中相互交叉引入#import会报错,但是在.m中相互引用不会报错
循环retain问题
循环retain的使用:
循环的retain会导致两个对象都会内存泄露
防止方法:
1、让某个对象多释放一次
2、一端使用assugn一端使用retain
示例:
Person.h
引入头文件时必须用@class
<span style="font-size:14px;">@property(nonatomic,retain)Person *owner;
Dog.m
-(void)dealloc{
[_owner release];
NSLog(@"Dog已经挂啦") ;
[super dealloc];
}</span>
Dog.h
引入头文件时必须用class
<span style="font-size:14px;">@property(nonatomic,retain)Person *owner;
Dog.m
-(void)dealloc{
[_owner release];
NSLog(@"Dog已经挂啦") ;
[super dealloc];
}</span>
<span style="font-size:14px;">int main(){
Person *p=[Person new];
Dog *d=[Dog new];
p.dog=d;
d.owner=p;
[p release];
[d release];
}</span>
这就会造成内存泄露。像上面那样改变就好了。