前提
(1). 以下是针对cocoa对象,不包括core foundation
(2). cocoa对象都是用引用计数来跟踪对象的内存使用情况的。
(3). 在子类里面父类先初始化和后释放的原则。自己想下为什么
栈空间和堆空间的区别。
我们说的内存管理都是基于堆空间的,因为函数内的栈空间是由编译器自己控制的。
关于core foundation的内存管理,在下下一次介绍。
内存管理主要是解决以下两个问题:
(1). 内存泄漏,内存泄漏就是不再使用的对象内存没有及时释放,导致程序内存使用过量,可能影响程序的效率,严重过量时可能导致程序被ios系统终止。
(2). 释放或者重写正在使用的数据。引发内存损坏,会让程序崩溃或者损坏
ios内存管理模型分为MRR(
manual retain-release ) 和 ARC(
Automatic Reference Counting )
MRR (手动retain和release)
你需要自己控制你拥有的对象的内存的分配和释放。
内存管理基本原则
(1). 你拥有你创建的任何对象(以下名称开头的方法,alloc,copy,retain,mutableCopy,new等)
(2). 你拥有你retain的对象
(3). 你必须释放你拥有的不再需要使用的对象(release)
(4). 你不能释放不是你拥有的对象。
exp.
- (
NSString
*)fullNameOne
{
NSString *string = [[[ NSString alloc ] initWithFormat : @"%@" , @"123" ] autorelease ];
return string;
}
{
NSString *string = [[[ NSString alloc ] initWithFormat : @"%@" , @"123" ] autorelease ];
return string;
}
- (
NSString
*)fullNameTwo
{
NSString *string = [ NSString stringWithFormat : @"%@ %@" , @"123" , @"hello" ];
return string;
}
- ( NSString *)fullNameThree
{
NSString *string = [[ NSString alloc ] initWithFormat : @"%@" , @"123" ];
return string;
}
{
NSString *string = [ NSString stringWithFormat : @"%@ %@" , @"123" , @"hello" ];
return string;
}
- ( NSString *)fullNameThree
{
NSString *string = [[ NSString alloc ] initWithFormat : @"%@" , @"123" ];
return string;
}
以上第一种和第二种没有问题,第三种存在内存泄漏的可能,因为调用fullName的人并不知道返回的string使用完了还自己手动需要释放。
为什么第二种方式没有alloc,调用alloc和没有调用alloc有什么区别?读者仔细想下。
使用accessor方法来简化内存管理
@interface
Memory :
NSObject
@property ( nonatomic , strong ) NSString *username;
@property
(
nonatomic
,
weak
)
IBOutlet UILabel
*usernameDisplayArea;
#import
“Memory.h"
{
[person retain ]; //why need retain here …. 作用,当 person 传进来的时候, person 没有被释放,但是当我们正在使用这个对象的时候,这个 person 对象可能在外面被释放了,我们的程序会因此崩溃。因此为了防止意外释放,我们要先retain这个对象,确保我们使用的过程中不会出错。
NSString *username = [NSString stringFormatWith: @"%@ living in room 32." , person.name];
[person release ];
return username;
@end
@implementation
Memory
@synthesize username = _username ;
@synthesize usernameDisplayArea = _ usernameDisplayArea ;
@synthesize username = _username ;
@synthesize usernameDisplayArea = _ usernameDisplayArea ;
@end
;
以上代码有几个地方需要,1. strong和weak 2.
@synthesize
username =
_username
;的含义。username是属性名,而_username是你成员变量的名字。使用时,self.username实际是调用你的属性username的getter方法,而_username仅仅就是在使用你的成员变量。
tip:
不要在init和dealloc种使用accessor方法,原因:因为在init时候,你的对象还没有构造完成,所以不能使用当前没有构造完成的对象,会有不确定的时候发生。
弱引用用得最普遍的是IBOutlet,所有的IBOutlet都是交给了storyboard和xib文件进行管理的。
在MRR中,如何手写setter方法?
- (void)setUsername:(NSString *)newUsername
{
NSLog(@"when call setter!");
[newUsername retain]; //这个地方暗藏玄机,retain必须要在username的release之前,因为newUsername可能和username指向的同一个地方。
[usernamerelease];
username = newUsername;//指针的赋值,让他们两个指向同一个地方。
//self.username = [myUsername retain]; //why here can not use self.username,这里会抛错误。因为使用self.username = xxx时候就是使用了setter
}
有两个点需要注意:
第一,如果属性是bool类型,那么getter方法的名字应该是isXXX,其他情况都是XXX
第二,setter方法,注意看上面这个setter方法。为什么不需要判断username是不是空呢?因为在oc中,发送消息给nil是没有问题的,不会发生任何事情。
以下代码有个小tip:
- (
NSString
*)getCurrentUsername:(Person *)person{
[person retain ]; //why need retain here …. 作用,当 person 传进来的时候, person 没有被释放,但是当我们正在使用这个对象的时候,这个 person 对象可能在外面被释放了,我们的程序会因此崩溃。因此为了防止意外释放,我们要先retain这个对象,确保我们使用的过程中不会出错。
NSString *username = [NSString stringFormatWith: @"%@ living in room 32." , person.name];
[person release ];
return username;
}
tips:
如果发送消息给一个已经被释放的对象,程序会崩溃。
避免释放你正在使用的对象,需要注意以下两个情况
1. 当一个对象从一个基础的集合中释放时
2. 当对象的父对象被释放时
autorelease 是延迟发送release消息给你的对象。最近一层的autorelease pool释放时,对应的release消息被发送。
autorelease pool 的问题,在arc中已经被@autoreleasepool{} blocks取代了。
autorelease 解决了以下几个问题:
autorelease pool降低了应用程序的peak memory footprint,需要使用autorelease pool来管理产生的线程,因为线程保持自己的栈空间。
为避免过高的footprint,我们可以在分配适当量的临时变量后,drain掉pool。
- (
void
)autoreleasePoolTest
{
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc ] init ];
for ( int i = 0 ; i < 1000000 ; i++) {
NSString *s = [[[ NSString alloc ] initWithFormat : @"%@" , @"poolTest" ] autorelease ];
NSLog ( @"%@" , s); //using some temp obj.
if (i % 100 == 1 ) { //you can decide the number.
[pool drain ];
pool = [[ NSAutoreleasePool alloc ] init ];
}
}
[pool drain ];
}
{
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc ] init ];
for ( int i = 0 ; i < 1000000 ; i++) {
NSString *s = [[[ NSString alloc ] initWithFormat : @"%@" , @"poolTest" ] autorelease ];
NSLog ( @"%@" , s); //using some temp obj.
if (i % 100 == 1 ) { //you can decide the number.
[pool drain ];
pool = [[ NSAutoreleasePool alloc ] init ];
}
}
[pool drain ];
}
ARC 自动引用计数
取消了显示的retain和release,autorelease等,编译器在编译阶段自动插入了retain和release这些语句。
ARC的主要目的是希望开发人员投入更多的精力去关注自己要实现的功能,把你从内存管理的泥潭中解放出来。
ARC中引入了弱引用(weak)。弱引用在对象还拥有强引用前能使用引用的对象。
弱引用主要是为了解决retain cycle
问题。循环引用的问题,对象之间相互拥有。
Cocoa建立了一个约定,父对象必须保持对子对象的强引用,子对象必须保持对父对象的弱引用
写得比较乱,因为是按照apple文档,边写边思考写的。
下一篇blog专门介绍ARC.