一、课程目标:
1、掌握内存管理的原则
2、深入理解属性的内存管理
3、掌握便利构造器内存管理
4、掌握NSAutoreleasePool(自动释放池)的使用
二、如何管理内存?
1、解决办法:
有创建,就要有销毁
所谓 内存管理:针对创建和销毁过程进行管理
2、管理方式:
垃圾回收(java、C#等)
人工管理(OC、C、C++)
自动管理内存(ARC、IOS5新特性),并非垃圾回收
3、引用计数:
OC采用“引用计数机制”(retainCount)管理对象所占用的内存
计数:计数器,用于统计数字。例如:1、2、3...等
引用计数:某一块内存,拥有这块内存的拥有着的个数。
注意:在OC语言中,NSObject类以及子类的实例对象包含一个整型属性,用来统计当前的引用计数
4、生命周期
实例对象的生命周期
出生于alloc方法
死亡于dealloc方法
注意:通过对类发送alloc消息,获得的实例对象的引用计数赋值为1!
注意:当实例对象的引用计数为0时,自动对该实例对象发送dealloc消息!
5、常见方法
copy制造一个副本,将副本的引用计数赋值为1,拥有副本的所有权。
retain对象引用计数+1,并拥有对象所有权。
release对象引用计数-1,并放弃对象所有权。
autorelease未来某个时间对象引用计数-1,并放弃对象所有权。
引用计数的变化
Student * stu = [[Student]init];
NSLog(@“创建时计数为:%lu”,[stu retainCount]);
[stu retain];//调用了retain方法,计数器应该加1
NSLog(@“retain后计数为:%lu”,[stu retainCount]);
[stu release];//计数器减一
[stu release];//计数器再减一
6、指针变量与对象
对象是放在堆里的
指针变量时放在栈里的
唯一的联系:指针变量存的是对象的地址
7、对象的实例属性
@property Student * stu;
//设置器
- (void)setStu:(Student *)stu{
<span style="white-space:pre"> </span>_stu = stu;
}
//访问器
- (Student)stu{
<span style="white-space:pre"> </span>return _stu;
}
代码执行步骤:
然后执行:[student release];
然后执行:[mc.stu sayHi];
注意:地址为0x10ff78c的student对象已经被销毁,无法接收sayHi消息,程序崩溃!
为了解决崩溃:
@property(retain,nonatomic) Student * stu;
//设置器
- (void)setStu:(Student *)stu{
<span style="white-space:pre"> </span>[stu retain];
<span style="white-space:pre"> </span>[_stu release];
_stu = stu;
}
//访问器
- (Student)stu{
return _stu;
}
代码执行步骤:
注意:地址为0x10ff78c的student对象没有被销毁,可以接收sayHi消息,程序正常!
dealloc方法在对象引用计数为0的时候自动调用
主要用于释放自身所占有的资源,永远不要手动调用dealloc。
8、便利构造器内存管理
阅读下面代码,看内存是否有问题
+ (id)studentWithName:(NSString *)name
andAge:(NSInteger)age
andHobby:(NSString *)hobby
andAddress:(<span style="font-family: Arial, Helvetica, sans-serif;">NSString *</span>)address
{
Student *student = [[Student alloc]initWithName:name
andAge:age andHobby:hobby andAddress:address];
[student release];
return student;
}
注意:地址为0x10ff78c的student对象已经被销毁,虽然将地址返回,但已经不能接受消息!!!
那么便利构造器要怎么进行内存管理呢,我们需要用到自动释放池
自动释放池:
NSAutoreleasePool类
当创建的对象未来某个时候销毁时,可以使用对象的autorelease方法
对象将所有权交给最近的NSAutoreleasePool对象
当池对象drain或release时,会逐一对池内对象发送release消息
尽量不要使用autorelease,而是使用release
便利构造器
+ (id)studentWithName:(NSString *)name
andAge:(NSInteger)age
andHobby:(NSString *)hobby
andAddress:(NSString *)address
{
Student *student = [[Student alloc]initWithName:name
andAge:age andHobby:hobby andAddress:address];
return [student autorelease];//不立即释放,而是交给autoreleasepool
}
自动释放池
//较早版本的创建
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];<pre name="code" class="objc">Student * stu = [[Student alloc]init];//alloc一个对象
[stu autorelease];// 将所有权控制交给pool,stu不用手动release了
...
[pool release];
//现版本的创建
@autoreleasepool{
Student * stu = [[Student alloc]init];//alloc一个对象
[stu autorelease];// 将所有权控制交给pool,stu不用手动release了
...
}
示例1:
+ (id)personWithName:(NSString *)aName{
Person * person = [[Person alloc]initWithName:aName];
return [person autorelease];
}
示例:
- (void)printName{
NSString *name = @"hello";
NSLog(@"%@",name);
}
- (void)printHello{
NSString *str = [NSString stringWithFormat:@"Hello"];
NSLog(@"%@",str);
}
9、内存检测工具
Analyze内存静态分析(编译期)
leaks内存泄露检测工具(运行期)
三、总结
内存管理原理:
凡是alloc、retain、copy的地方,都应出现release或autorelease与之对应
属性为retain或copy的话,需要在类的dealloc中释放这个属性
便利构造器本身应包含autorelease
一定不要释放没有所有权的对象。不要手动调用dealloc
四、术语与技巧
ios程序开发离不开内存管理。虽然原则很简单,但是真正做好内存管理还是比较困难的。
法宝:只要自己使用了alloc、retain、copy,就一定要有对应的release、autorelease,它们要配对使用。
工具:Analyze和Instruments(leaks)
必杀技:dealloc中打印NSLog,看dealloc是否执行