- 移动设备的内存极其有限,每个app 所能占用的内存是有限制的
- 当app所占用的内存较多的时候系统会发出内存警告,这时候就需要在回收一些不需要在使用的内存空间,比如回收一些不需要使用的对象,和变量等
- 管理范围:任何继承了NSObject的对象,其他基本数据类型(int,char,float,dauble,struct,enum等)无效
- 每个OC对象都有自己的引用计数器,是一个整数,表示对象被引用的次数,即有多少人在引用这个OC对象
- 每个对象内部都有四个字节的存储空间来存储引用计数器
- 当使用alloc,new,和copy创建一个对象的时候,新对象的引用计数器默认就是1
- 当一个对象的引用计数器为0时,对象占用的内存就会被系统回收,如果对象的计数器不为0,那么在整个程序运行的过程中它占用的内存就不可能被回收除非整个程序已经退出
- 给对象发送一条retain消息,可以使计数器的值+1,(retain方法返回对象的本身)
- 给对象发送一条release消息,可以使计数器的值减一
- 给对象发送一条retainCount消息,获取当前引用的计数器的值
- 当一个对象的引用计数器为0时候,那么它将被销毁,其所占用的内存被系统回收
- 当一个对象被销毁的时候,系统会自动向对象发送一条dealloc消息
- 一般会重写dealloc方法,在这里释放相关资源,dealloc方法,就像对象的遗言
- 一旦重写了dealloc方法,就必须调用[super dealloc]方法,并且放在最后面调用
- 不要直接调用dealloc方法
- 一旦对象被回收了,那么它占用的内存就不再使用,坚持使用会导致程序崩溃,(野指针错误,野指针:该指针指向不可用的内存的指针叫野指针)oc中不存在空指针错误,给空指针发送消息不会报错
@implementation Student
// 基本数据类型 直接复制
- (void)setNo:(int)no
{
_no = no;
}
- (int)no
{
return _no;
}
// OC对像类型 先判断是不是新传的对像,对旧的对象做一次release,对新的对象做一次retain
- (void)setName:(NSString *)name
{
if(_name != name)
{
[_name release];
_name = [name retain];
}
}
- (NSString *)name
{
return _name;
}
- (void)setCar:(Car *)car
{
if(_car != car)
{
[_car release];
_car=[car retain];
}
}
- (Car *)car
{
return _car;
}
- (void)setDog:(Dog *)dog
{
if(_dog != dog)
{
[_dog release];
_dog = [dog retain];
}
}
- (Dog *)dog
{
return _dog;
}
// dealloc代码规范 一定要调用 [super dealloc];并且放在最后面调用
- (void)dealloc
{
[_name release];
[_car release];
[_dog release];
[super dealloc];
}
@end
@property参数
1.set方法内存管理的相关参数
retain : release旧值,retain新值(适用于OC对象类型)
assign : 直接赋值(默认的,适用于非OC对象类型)
copy : releate旧值,copy新值,一般用于NSString
2.是否生成set方法
readwrite : 同时生成setter和getter的声明,实现(默认)
readonly : 只声明getter的声明,实现
3.多线程管理
nonatomic : 性能高(一般使用这个)
atomic : 性能低(默认)
4.setter和getter方法的名称
setter : 决定了set方法的名称,一定要有一个冒号 :
getter :决定了get方法的名称(一般用在BOOL类型的方法)
循环引用
1. @class
使用场景:
对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类
同时在A的头文件中引用B的头文件和在B中引用A的头文件,这种代码编译会报错
使用@class在两个类相互声明,就不会出现编译错误
用法;
使用@class类名;就可以引用一个类,说明一下它使一个类
和#import的区别
1.#import方式会包含所被引用类的所有信息,包括被引用类的变量和方法,@class只是告诉编译器
在A.h文件中B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中正在要用到
时,才会真正查看B类中的信息
2.如果有上百个头文件都#import了同一个文件,或者这些文件被一次#import,那么一旦最开始
的头文件稍有改动,后面引用到这个文件的所有类都要重新编译一遍,效率很低,使用@class就不会出现这种问题了
3.在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
循环retain
比如A对象retain了B对象,B对象retain了A对象
这样会导致A对象和B对象永远无法释放
解决方案:
当两端互相引用时,应该一端用retain,一端用assign
一:autorelease的基本用法
- 会将对象放到一个自动释放池中
- 当自动释放池被销毁的时候,会对池子里面的所有对象做一次release操作
- 会返回对象的本身
- 调用完autorelease方法后,对象的计数器不变
- 不用关心对象的释放时间
- 不用关心什么时候调用release
- 占用内存较大的对象不要随便使用autorelease
- 占用内存较小的对象使用autorelease没有太大的影响
- alloc之后调用了autorelease,有调用了release
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
// 0
[p release];
}
- 连续多次调用autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
五:自动释放池
- 在IOS程序运行的过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后出)
- 当一个对象调用autorelease方法的时候,会将这个对象放到栈顶的释放池
- IOS5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//自动释放池开始
Person *pp = [[[Person alloc] init] autorelease];
[pool release]; //自动释放池结束
- IOS5.0开始
@autoreleasepool
{
}
七:autorelease的应用
1.系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
2.开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
1> 创建对象时不要直接用类名,一般用self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
ARC机制
基本简介
ARC是自ios5之后增加的新特性,完全消除了手动管理内存的繁琐编译器会自动在适当的地方插入适当的retain,release, autorelease,
让开发者不需要关心内存管理的问题
ARC是编译器特性,而不是ios运行时特性,它也不是其他语言中的垃圾回收机制,因此ARC和手动内存管理性能是一样的,有时可以更加快速,应为编译器可以执行某些优化
基本原理
规则:
ARC的规则非常简单:只要还有一个强指针变量指向对象,对象就会保持在内存中
强指针:
默认所有实例变量和局部变量都是Strong指针
弱指针指向的对象被回收后弱指针自动变为nil指针,不会引发野指针错误
使用注意:
1.不能调用release,retain,autorelease,retainCount
2.可以重写delloc,但是不能调用[super dealloc]
3.@property:想长期拥有某个对象,应该用strong,其他对象用weak
4.其他基本数据类型依然用assign
5.两端互相引用时,一端用strong,一端用weak