基本原理:
1、什么是内存管理
-》移动设备的内存极其有限,每个app所能占用的内存是有限制的
-》当app所占用的内存比较多时,系统会发出内存警告,这时得回收一些不需要在使用的内存空间,比如回收一些不需要使用的对象、变量等
-》管理范围:任何继承了NSObject的对象。对其他基本数据类型(int、char、float、double、struct、enum等)无效
局部变量都放在了内存中的栈空间中,对象放在堆空间中
堆空间中的东西是动态分配的,需要我们手动回收。栈空间不用我们自己管
2、对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数,表示"对象被引用的次数",即有多少人正在使用这个OC对象。(分配4个字节的存储空间存放引用计数器)。当对象刚刚产生的时候引用计数器是1
3、引用计数器的作用
当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认是1
当一个对象的引用计数器值为0是,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出。
4、引用计数器的操作
-》给对象发送一条retain消息,可以使引用计数器值 +1 (retain方法返回对象本身)
-》给对象发送一条release消息,可以使引用计数器值 -1 (没有返回值)
-》可以给对象发送 retainCount消息,获得当前的引用计数器值
关闭ARC(Automatic Reference Counting):
选中项目名称→Building Settings→Levels→Apple LLVM 6.0 -Language -Objective C将下面的YES改为no
NSUInteger:相当于long,输出的时候占位符使用%ld
僵尸对象:引用计数器变成0的对象,将不能使用,称为僵尸对象
野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错
空指针:没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
错误:EXC_BAD_ACCESS:访问了一块坏的内存(已经被回收、已经不可用的内存);
为了防止访问坏内存的错误,我们可以在对象的计数器的值减为0之后,将指针赋空值
p=nil;//将指针p变成空指针
OC中不存在空指针错误,给空指针发送消息,不会报错
-[Person setAge:]: message sent to deallocated instance 0x100202230
给已经释放的对象发送了一条setAge:消息
5、对象的销毁
-》当一个对象的引用计数器值为0时,那么他将被销毁,其占用的内存被系统回收
-》当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
-》一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
-》一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
-》不要直接调用dealloc方法
-》一旦对象被回收,它占用的内存就不能再用,坚持使用会导致程序崩溃(野指针错误)
内存管理原则
对对象进行一次retain,那么使用结束的时候就要release
谁创建,谁release。如果你通过alloc、new创建一个对象,那么你必须调用release
谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release
你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
你不想再使用(占用)某个对象,就应该让对象的设计器-1(让对象做一次release)
在类的实现中获取成员变量值方法:
_speed:直接访问成员变量
self->_speed:直接访问成员变量
self.speed:get方法
[self speed]:get方法
内存管理代码规范:
1、只要调用了alloc,必须有release(aotorelease)
如果对象不是通过alloc产生的,就不需要release
2、set方法的代码规范
-》基本数据类型:直接赋值
- (void)setAge:(int)age
{
_age=age;
}
-》OC对象类型
- (void)setCar:(Car *)car
{
//1、判断是不是新传进来的对象
if(car != _car)
{
//2、对旧对象做一次release
[_car release];
//3、对新对象做一次retain
_car = [car retain];
}
}
3、dealloc方法的代码规范
-》一定要[super dealloc],而且要放到最后面
-》对self(当前)所拥有的其他对象做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
@property的内存管理
@property (retain) Book *book;
生成的set方法如下:
- (void)setBook:(Book *)book
{
if(_book != book)
{
[_book release];
_book = [book retain];
}
}
1、set方法内存管理相关参数:
retain :release旧值,retain新值(适用于OC对象类型)
assign:直接赋值(默认,适用于非OC对象类型)
copy :release旧值,copy新值
2、是否要生成set方法
readwrite:同时生成setter和getter的声明、实现(默认)
readonly :只会生成getter的声明、实现
3、多线程管理
nonatomic:生成set方法的时候不要加多线程代码。性能高(一般就用这个)
atomic :生成set方法的时候加多线程代码。性能低(默认)
4、setter和getter方法的名称
setter:决定了set方法的名称,一定要有一个冒号(不常用)
getter:决定了get方法的名称 (一般用在BOOL类型)
@property int age;生成的方法名为:age,setAge:
@property (getter=abc , setter=setAbc: ) int age;
生成的方法名:get方法:abc
set方法:setAbc:
long表示时间类型的时候,是从1970-01-01 00:00:00开始,一共过了多少毫秒
循环引用:
涉及到循环引用(你中有我,我中有你),添加引用的时候使用@class 类名
1、@class的作用:仅仅告诉编译器,某个名称是一个类
@class Card;//仅仅是告诉编译器,Card是一个类
2、开发中引用一个类的规范
-》在 .h 文件中用@class来声明类
-》在 .m 文件中用#import来包含类的所有东西
使用@class可以提高编译器编译效率(当一个类被多个类引用的时候,如果类发生变化,那么需要重新拷贝)
3、两端循环引用解决方案:
-》一端用retain
-》一端用assign
autorelease:半自动释放
@autoreleasepool
{//{ 开始代表创建了释放池
Person *p = [[[Person alloc] init] autorelease];
p.age = 19;
}// }结束代表销毁释放池
注意:@autorelease是可以嵌套的,是以栈(先进后出)的方式存储的
1、autorelease的基本用法
-》会返回对象本身
-》会将对象放到一个自动释放池中
-》当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
-》Person *p = [[[Person alloc] init] autorelease]; 调用完autorelease方法后,对象的计数器不变
2、autorelease的好处
-》不用在关心对象释放的时间
-》不用再关系什么时候调用release
3、autorelease的使用注意
-》占用内存较大的对象不要随便使用autorelease
-》占用内存较小的对象使用autorelease,没有太大影响
4、错误写法
-》alloc之后调用autorelease,有又调用release
@autoreleasepool
{
Person *p = [[[Person alloc] init] autorelease];
[p release];
}
-》连续调用多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
5、自动释放池
-》在IOS程序运行过程中,会创建无数个池子。这些池子都是以栈结果存在(先进后出)
-》当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6、自动释放池的创建方式
-》IOS5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *pp = [[[Person alloc] init] autorelease];
[pool release];
-》ios5.0开始
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
1、系统自带的方法里没有包含alloc、new、copy,说明返回的对象都是autorelease的
NSString *str = @"13423";这种方式创建出来的字符串对象,默认就是autorelease的。不需要我们去管理内存
2、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
-》创建对象时不要直接使用类名,一般用self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
总结:
一、计数器的基本操作
retain:+1
release:-1
retainCount:获得计数器
二、set方法的内存管理
-》set方法的实现
- (void)setCar:(Car *)car
{
if(_car != car)
{
[_car release];
_car = [car retain];
}
}
-》dealloc方法的实现
- (void)dealloc
{
[_car release];
[super dealloc];
}
三、@property参数
-》OC对象
@property (nonatomic, retain) 类名 *属性名;
@property (nonatomic, retain) Car *car;
@property (nonatomic,retain) id car;
被retain过的属性,必须在dealloc方法中release属性
-》非OC对象
@property (nonatomic,assign) 类型名称 属性名;
@property (nonatomic,assign) int age;
四、autorelease
-》系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都已经autorelease过的
[NSString stringWithForm:.........]
[NSDate date];
-》开发中经常写一些类方法快速创建一个autorelease的对象
创建对象的时候不要直接使用类名,使用self