做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作的理解是衡量一个程序员成熟与否的一个标准。好了,闲话不说,下面进入正题。 众所周知,ObjectiveC的内存管理引用的一种叫做“引用计数“ (Reference Count)的操作方式,简单的理解就是系统为每一个创建出来的对象,(这里要注意,只是对象,NSObject的子类,基本类型没有‘引用计数’)记录一个引用计数,初始化这个对象的时候会调用alloc方法,系统在alloc方法里会将这个对象的引用计数+1;例如:UIView *myview = [UIView alloc] init]; 这里创建的对象myview它现在的引用技术就是1。所有的对象可以调用[xxx reatin];来使自身的引用计数+1。也可以调用[xxx release];让自身的引用计数-1.如果一个对象的引用计数为0,那么这个对象立即(记住,是立即)会被系统回收掉。除了[xxx reatin];会将对象的引用计数+1之外还有很多方法会将对象的引用技术+1,举两个最常用的:[self.view addSubView:myView];这个方法会将myView的引用计数+1。 NSArray类里的[array addObject:myView];也会将引用计数+1。下面我会用代码说明一下几个比较容易出错的地方。
(一)临时变量对象的引用计数说明
UIView *v = [[UIView alloc] init]; //分配后引用计数为1
- [self.view addSubview:v]; //这儿引用计数加1,为2
- [v release]; //这儿引用计数为-1为1
- 最后系统在回收self.view的时候,会先回收其subView,所以self.view被回收时v的引用计数是0,v就会立即被回收。
- v = [[UIView alloc] init];
- [self.view addSubview:v];
- [v release];
- 如果在dealloc里调用了[v release];那么就多release了,会crash.
- @property (nonatomic, assign) UIView *v; 这儿是assign, 然后分配内存的时候如果是这样
- self.v = [[UIView alloc] init];//计数为1
- [self.view addSubview:self.v];//计数为2
- [self.v release];//计数为1
- 这时不需要在dealloc里[self.v release];因为assign声明的对象引用计数不会自动+1.当self.view被回收时,self.v的引用计数就会变成0;V就会被系统回收掉。
- @property (nonatomic, retain) UIView *v; 或 @property (nonatomic, copy) UIView *v;声明的属性,那么这样分配内存
- v = [[UIView alloc] init];
- [self.view addSubview:v];
- [v release];这样与a是一样情况,不需要在dealloc里释放。
- 但如果是
- self.v = [[UIView alloc] init];
- [self.view addSubview:self.v];
- [self.v release];加了个self,那么就要在dealloc里[v release];因为如果属性用reatin声明的时候,用self.初始化的时候对象的引用技术会自动+1。self.v = [[UIView alloc] init];//这个时候引用计数为2
下面聊聊autorelease,autorelease是objectiveC语言的一个比较特殊的机制,也是理解ObjectiveC内存管理的关键,Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对的,其实autorelease就是一个延迟释放的机制,比较好的介绍autorelease的文章:http://developer.51cto.com/art/201007/212523.htm 这篇文章个人认为autorelease讲解的比较详细,也易于理解。推荐大家看看,这里除了这篇文章之外我还要谈谈我对autorelease的一些理解。我理解的是,系统在执行每一个函数的时候,会自动开启一个autoreleasePool(记住是每一个函数的运行都会开启一个autoreleasePool,而不是每一个类开启一个,这一点很关键),在这个函数当中创建的所有用autorelease声明的对象都会放进这一个的池当中。当函数运行完的时候,这一个autorelasepool 会被drain 掉,那么池中所有引用计数为0的(千万记住是引用计数为0)对象,就会被系统回收掉。写到这里很多人就会程序猿就会质疑我这个说法了,比如我在某一个UIViewController类的viewDidLoad函数里面写了:UIView *myView = [[UIView alloc] init] autorelease];[self.view addsubView:myView]; 的时候,当viewDidLoad函数执行完,为什么myView对象没有被回收。这是因为:因为调用了[self.view addsubView:myView];所以这时myView的引用计数变成了2,当函数执行完,autorelease被drain掉的时候,自动释放池内所有的对象都会调用release方法,这时所有的对象的引用计数-1,如果这时对象的引用计数为0那么它会被系统回收,如果不为0,那么他还回被保留,也就是说自动释放池中的对象不会因为自动释放池的销毁而被销毁,自动释放池的对象只是被延迟释放了,池中对象的释放也得满足引用计数的机制。