为什么要内存管理?
因为内存一直被占用的话,内存最终会不够用。
内存管理好处,1G可以运行3G应用,只要使用时不超过1G,及时释放的话。
一.oc中采用“引用计数”(retainCount)方式管理对象所占内存(内存有指针指向的概念)。
- alloc为对象分配内存。dealloc为对象释放所占内存,不能手动调用。
- 使用alloc、new对象时,并将其引用计数器设为1,并拥有对象所有权。
- copy制造一个副本,并将副本的引用计数设为1,对原有的引用计数不影响,并拥有副本对象所有权。
- retain使引用计数加1,并拥有对象所有权。release使引用计数减1,并放弃对象所有权。
- 当对象的retainCount为0时,系统自动调用dealloc释放对象内存;否则,分配内存将一直被占用。
- autorelrease向NSAutoreleasepool注册。
二.
- 引用计数:计内存的使用次数(内存被用了多少次)
- 所有权对象分为:程序员和系统。谁retain,所有权归谁。 NSString *sonCar = [mamaCar retain];//儿子有所有权,此时retain引用计数+1了,使用完后销毁不会出现野指针 NSString *friendCar = sonCar;//朋友没有使引用计数加1,也就是说当引用计数为0,对象销毁的时候,会出现friendCar这个野指针
- 消息(方法)是有延迟的,系统回收可能存在延迟,因为系统回收是以轮询的方式。就像现实中的保洁工人收垃圾。
- 引用计数为0是打印不出来的,因为0代表没有该对象了,所以retain方法调用不出来,这时候系统只能打印出1。
- 原则使用后立即释放。
三.属性中内存管理
- 原则:一个类中,如果这个类有属性声明retain或者copy的属性,那么我们需要在这个类的dealloc方法里释放一次。
- 属性内存管理分析图
- 属性的赋值方式:
- 先初始化对象a
- b.a = a;
- [a release];
四.便利构造器内存管理
- 通过使用autorelease延迟release来管理构造器的对象。return [对象 autorelease];或@autoreleasepool{}
- NSAutoreleasePool自动释放池:
- 当创建的对象未来某个时候销毁时,可以使用对象的autorelease。
- 对象将所有权管理交给最近的NSAutoreleasePool对象,并由其全权管理。栈式结构(UINavigationController和图形上下文)
- 当池对象drain或release时,会逐一对池内对象发送release消息。
- 能不使用,尽量不使用aoturelease。知道什么时候用完,直接release。
- 任何OC对象只要调用autorelease方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池)
五.检测工具
- static Analyzer-Analysis Policy
- product-》profile-》leaks
六.类中self点出来的属性。需要autorelease。dealloc中不用[_xxxx release]。
相反:_xxxx = [[** alloc]init];的属性在dealloc中[_xxxx release];。不用autorelease。
Teacher.h
@interface Teacher : NSObject -(void)onClass; @end
Teacher.m
#import "Teacher.h" @implementation Teacher -(void)onClass { NSLog(@"teacher is on class"); } @end
Student.h
#import "Teacher.h" @interface Student : NSObject { Teacher *_teacher; } //@property(nonatomic,retain)Teacher *teacher; -(void)setTeacher:(Teacher *)aTeacher; -(Teacher *)teacher; @end
Student.m
#import "Student.h" @implementation Student //@synthesize teacher = _teacher; //retain中@synthesize默认手动写法 -(void)setTeacher:(Teacher *)aTeacher { if (_teacher != aTeacher)//旧对象不等于新对象的时候-赋新值 { [_teacher release];//_teacher释放?第一次_teacher是nil(对nil release没影响),但是如果后面还要把对象赋给_teacher的话,这里必须先释放(对上次传入的对象先进行释放并放弃所有权) _teacher = [aTeacher retain];//把所有权给_teacher。copy的话这里也改为[aTeacher retain] } } -(Teacher *)teacher { return _teacher; } -(Student *)init { self = [super init]; if (self) { } return self; } //重写 -(void)dealloc { [_teacher release];//属性中retain,这里也要release NSLog(@"我(%@)快要被释放了。。。。。",self);
[super dealloc];//第一步必须做的,保留父类释放
} @end
AppDelegate.m
/* Student *s = [[Student alloc]init]; NSLog(@"reatinCount = %d",s.retainCount); [s retain]; NSLog(@"reatinCount = %d",s.retainCount); [s release]; NSLog(@"reatinCount = %d",s.retainCount); [s release]; NSLog(@"reatinCount = %d",s.retainCount); */ //属性的内存管理 Teacher *t = [[Teacher alloc]init]; Teacher *t2 = [[Teacher alloc]init]; Student *stu = [[Student alloc]init]; stu.teacher = t; stu.teacher = t2; [t release]; [t2 release]; //sleep(10);//让系统休眠10秒 [stu.teacher onClass]; [stu release]; /* Student *s1 = [[Student alloc]init]; Student *s2 = s1; NSLog(@"s2 = %d",s2.retainCount); [s2 retain];//2 NSLog(@"s1 = %d",s1.retainCount); NSLog(@"s2 = %d",s2.retainCount); [s1 release]; NSLog(@"s1 = %d",s1.retainCount); NSLog(@"s2 = %d",s2.retainCount); */
属性与构造器手动内存写法
AInstance.h
#import <Foundation/Foundation.h> @interface AInstance : NSObject { NSString *_name; int _age; int _cityCode; } -(void)setName:(NSString *)aName; -(NSString *)name; -(void)setAge:(int)aAge; -(int)age; -(void)setCityCode:(int)aCityCode; -(int)cityCode; -(AInstance *)initWithName:(NSString *)aName andAge:(int)aAge andCityCode:(int)aCityCode; +(AInstance *)aInstanceWithName:(NSString *)aName andAge:(int)aAge andCityCode:(int)aCityCode; @end
AInstance.m
#import "AInstance.h" @implementation AInstance -(void)setName:(NSString *)aName { if (_name != aName) { [_name release]; _name = [aName retain]; } } -(NSString *)name { return _name; } -(void)setAge:(int)aAge { _age = aAge; } -(int)age { return _age; } -(void)setCityCode:(int)aCityCode { _cityCode = aCityCode; } -(int)cityCode { return _cityCode; } -(AInstance *)initWithName:(NSString *)aName andAge:(int)aAge andCityCode:(int)aCityCode { self = [super init]; if (self) { _name = aName; _age = aAge; _cityCode = aCityCode; } return self; } +(AInstance *)aInstanceWithName:(NSString *)aName andAge:(int)aAge andCityCode:(int)aCityCode { AInstance *ainstance = [[AInstance alloc]initWithName:aName andAge:aAge andCityCode:aCityCode]; return [ainstance autorelease]; } - (void)dealloc { [_name release]; [super dealloc]; } @end
AppDelegate.m
AInstance *a = [[AInstance alloc ]initWithName:@"jobs" andAge:19 andCityCode:20]; NSLog(@"%@",a.name); [a release];
2.
NSAutoreleasePool
/*任何OC对象只要调用autorelease方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池)*/ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; for (int i = 0; i < 1000000; i++) { NSMutableString *str = [[NSMutableString alloc]init]; [str autorelease]; if (i%1000 == 0) { [pool release];/*该程序每执行1000次,就把池子销毁, 也就是str在自动释放池累积的对象不会超过1000个*/ pool = [[NSAutoreleasePool alloc]init];//同时新建一个池子 } } [pool release];