一、内存管理
什么是内存管理?为什么要管理内存?
解释:手机的内存空间是有限的,我们知道如果手机开的应用程序很多,则手机反应会很慢,其原因就是内存占用空间太大,导致系统反应慢。所以在开发中我们要管理内存,将一些不用的变量,或者对象销毁,释放其占用的内存,让其他程序使用。
在OC中我们只需要管理OC对象,OC对象时存放在堆中的,一般系统不会自动回收,所以需要我们开发人员销毁不用的对象。而一般的基本数据类型(int/float/double/char/enum/strut)是存放在栈中的,这些都是系统自动回收的,我们不需要管理。
二、引用计数器
1、引用计数器:每一个OC对象都有自己的引用计数器(占用4个字节),用来记录对象被引用的次数。2、引用计数器的管理:
1. 当使用alloc/copy/new创建一个新对象的时候,默认引用计数器为1;3、对象的销毁:
2. 对象调用retain方法,计数器+1;
对象调用release方法,计数器-1;
对象调用retainCount方法,返回计数器当期数值;
3.当对象的引用计数器数值为0,则系统会将该对象的内存回收
1.引用计数器为0,系统将回收该对象的内存;4、引用计数器方法的基本使用:
2.当对象被销毁时,系统会自动给对象发送一条dealloc消息(遗言);
3.一般我们会重写dealloc,在重写时一定要调用[super dealloc],且放在最后面;
1.retain :计数器+1,返回对象本身示例:
2.release:计数器-1,没有返回值;
3.retainCount: 返回计数器当期数值;
4.dealloc: 对象销毁时调用
Person *p = [[Person alloc] init]; //使用alloc创建,计数器为1 [p retain]; // 调用retain,计数器+1,计数器:2 [p release]; // 调用release,计数器-1,计数器:1 [p release]; // 调用release,计数器-1,计数器:0,对象p销毁,系统调用p对象的dealloc方法,此时p为野指针 //p.age = 10; // p已经被销毁,所以不能被赋值,如果写这句话,则手机会出现闪退现象,运行代码会出现 //message sent to dealloc instance 给已经释放的对象发送一条-setAge消息,系统找不到该方法,这是野指针错误
三、set方法的内存管理:
一般我们会将计数器的加减放在set方法中,因为set方法为赋值语句,当如果想给一个人的car变量赋值一辆车,则car对象就的计数器就应该加1,因为
有一个人正在使用car,所以car不能随随便便的销毁,所以我们需要修改set方法。
set方法一般为:set方法的代码规范:
给当前人的_car变量赋值,但是如果新车换旧车,则这种设计就不合理,例如给p.car=newcar重新赋值,人还是只有一辆车,但是旧车的计数器并未-1,所以set- (void)setCar:(Car *)car { _car = [car retain]; }
方法需要重新设计。
1.基本数据类型:直接复制dealloc方法的代码规范:
2.OC对象类型- (void)setAge:(int)age { _age = age; }
- (void)setCar:(Car *)car { if( car != _car ) //当旧车换新车 { [_car release]; //将当前车的计数器-1 _car = [car retain]; //当新车赋值给当前_car变量,旧车换新车 } }
1.[super release]一定要放在最后;
2.对self拥有的其他对象做一次release操作
示例:
- (void)dealloc { [_car release]; [super dealloc]; }
四、总结:
内存管理的原则:1>谁创建,谁release;2>谁retain,谁release
五、补充概念:
1.僵尸对象:所占内存已经被回收的对象,僵尸对象不能再使用。
2.野指针:指向僵尸对象的指针。
3.空指针:没有指向任何东西的指针(值为nil/NULL/0),给空指针发送消息不报错。
4.防止野指针的方法:p=nil,[p release];
5.野指针错误:EXC_BAD_ACCESS:访问一块坏内存(已经被回收的内存)
六、@property参数
@property参数1.内存管理相关的参数(与set方法相关)
1>retain : release旧值,retain新值(适用于OC对象类型)
2>assign : 直接赋值(默认,适用于非OC对象类型)
3>copy : release旧值,copy新值
2.是否要生成set方法
1>readwrite : 同时生成setter和getter的声明、实现(默认)
2>readonly : 只会生成getter的声明、实现
3.多线程管理
1>nonatomic : 性能高 (一般就用这个)
2>atomic : 性能低(默认)
4.setter和getter方法的名称
1>setter : 决定了set方法的名称,一定要有个冒号 :
2>getter : 决定了get方法的名称(一般用在BOOL类型),例如@property(getter = isRich) BOOL rich;
@property的一般写法:
@property(nonatomic, retain) NSString *name; @property(nonatomic, assign) NSString *name;
七、循环引用
1.@class仅仅是告诉编译器,某个名称是一个类
@class Car;仅仅是告诉编译器Car是一个类
2.开发中引用一个类的规范:
1>在.h文件中用@class来声明类;
2>在.m文件中用#import来包含类中所有的东西;
3.两端循环解决方案:
1>一端用retain
2>一端用assign
八、autorelease
autorelease(since ios5.0)
1.autorelease基本用法
1>autorelease会将对象放到一个自动释放池中2.autorelease的好处
2>当自动释放池被销毁时,会对池子里的所有对象做一次release操作
3>autorelease方法会返回对象本身
4>调用完autorelease方法后,对象的计数器不变
5>autorelease可以有多个@autoreleasePool { Person *p = [[[Person alloc] init] autorelease];// autorelease只是延迟release释放时间,但不会执行release p.age = 10; @autoreleasePool //@autoreleasePool可以有无限多个 { Person *p = [[[Person alloc] init] autorelease]; p.age = 20; } }
1>不用再关心对象释放的时间3.autorelease的使用注意
2>不用再关心什么时候调用release
1>占用内存较大的对象不要随便使用autorelease(因为不能控制对象释放的时间)4.错误写法
2>占用内存较小的对象使用autorelease,没有太大影响
1>alloc之后调用了autorelease,又调用release5.自动释放池
2>连续调用多次autorelease@autoreleasepool { Person *p = [[[Person alloc] init] autorelease]; // 计数器:1 [p release]; // 计数器:0,此时对象已经销毁 } // 再对对象做一次release,但是对象已经不存在了,野指针错误
@autoreleasepool { Person *p = [[[[Person alloc] init] autorelease] autorelease];//野指针错误 }
1> 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)6.自动释放池的创建方式
2> 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
1> iOS 5.0前autorelease的应用:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//在pool对象销毁前的操作
[pool release];
//这种方法只做了解,现在已经不用了
2> iOS 5.0 开始
@autoreleasepool
{
//这种方式简单,现在都应用此方法
}
注意://1. + (id)Person //方法名称与类名称相同 { //return [[[Person alloc] init] autorelease]; // 创建一个可以自动释放的对象 return [[[self alloc] init] autorelease]; //将Person改为self这样此方法可以应用多种类而不仅仅是Person类 } //2. + (id)personWithAge:(int)age //可以传递参数 { //Person *p = [[[Person alloc] init] autorelease]; Person *p = [self person]; p.age = age; return p; } int main() { @autorelease { Person *p = [Person person]; //创建person对象 //Student *s = [Student person]; //如果Student继承Person,则此person方法也可以调用 Person *p2 = [Person personWithAge:20]; } }
*系统自带的方法里面没有包含alloc、copy、new,说明返回的对象都是autorelease的;
*OC中不允许有两个类名称相同,例如连个Person,这是不对的。NSString中的NS是前缀;
*开发中经常提供一些类方法,用来快速创建一个已经autorelease的方法。
九、ARC
ARC:自动引用计数。这是编译器的特性,不是自动回收。ARC的判断准则:
强指针:默认情况下,所有的指针都是强指针__strong;只要没有强指针指向对象,就会释放。
弱指针:__weak,当对象只有弱指针指向时,系统会回收对象的内存空间。
ARC特点:
1>不允许调用release/retain/retainCount
2>允许重写dealloc,但不允许调用[super dealloc]
3>@property参数
*strong:相当于retain(适用于OC对象类型),成员变量为强指针
*weak:相当于assign,成员变量为弱指针
*assign:使用与非OC对象类型
两端引用时的解决方案:4>以前的retain改为strong
循环引用(weak指针的应用):@property(nonatomic, strong) Car *car;
@property(nonatomic, strong) Dog *dog; //Person.m @property(nonatomic, weak) Person *person; //Dog.m
*ARC:一端为strong,一端为weak
*非ARC: 一端为retain,一端为assign