一、课程目标:

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是否执行