详解ARC
- 从C理解引用计数器
- ARC的演变历程
- OC中的ARC
长达三个多月的项目,改了无数次版,到最后出来的产品和原先定义的完全不同。一直没有时间去好好的总结下。作为一枚程序员,只有不停的学习才能保持很好的学习状态。我学习一样东西很喜欢究根究底,可能是生性愚笨,就安慰自己勤能补拙吧。关于ARC我相信大家都深有了解了。可是面试的时候仍然有些问题卡壳,记得不够牢或者是理解得不够深刻。那么下面我将从C引出OC中的ARC,希望对大家能有帮助。也让我自己完完全全的掌握它。
作为第一篇博客,可能排版不太好看,多多谅解
1.从C理解内存管理
首先创建一个简易的C语言工程 OS X –>Application –>Command Line Tool
要使用C中手动分配内存要添加头文件#include <stdlib.h>
手动分配的内存在堆中。C语言项目比较OC来说更加繁琐,因为底层代码实现一个功能需要的代码量更多,往往一个项目组需要十几个人,所以当我们手动去管理内存的时候,A创建对象,B要使用,A并不知道B什么时候去使用内存,自己创建自己释放、A.B都要对方释放、A、B都以为对方没有释放,自己释放、等情况会造成以下内存问题:
- 内存提前释放
- 内存泄露
- 内存多次释放
就会造成程序崩溃。
#include <stdio.h>
#include <stdlib.h>
void test(int *p)
{
printf("调用了test方法\n");
free(p);
}
int main(int argc, const char * argv[]) {
int *p = malloc(4);
test(p);
free(p);
test(p);
return 0;
}
在上面代码块中,通过两次free(p)我已经释放掉了P,但是我在释放掉了p后仍然调用了这块内存,毫无疑问,会导致崩溃
那么我们如何精准的知道什么时候去释放p这块内存呢?
当p的引用次数为1的时候,我们就应该去调用free(p)了。那么我们需要其他成员在使用p时,将p的引用次数加一,使用完后减一,我们在判断当引用计数为1的时候,去调用free方法。这样就会避免上面三种内存问题。
那么我们需要一个值count来记录p的引用次数。
#include <stdio.h>
#include <stdlib.h>
int count = 0;
void release(int *p)
{
if (count>1) {
count--;
}else
{
free(p);
}
}
void test(int *p)
{
count ++;
printf("调用了test方法\n");
release(p);
}
int main(int argc, const char * argv[]) {
int *p = malloc(4);
count = 1;//创建p时 p的引用次数为1
test(p);
test(p);
test(p);
release(p);
return 0;
}
那么无论项目中的成员什么时候去调用p时,都不会产生内存问题。
那么又存在一个问题。当我们对象足够多的时候,如何做到对象和它的引用计数绑定呢?大家可能第一反应是字典,可是C中没有字典啊。 利用结构体可以解决这个问题,OC中一个对象自带的retainCount实现原理也就是这样。
2.ARC的演变历程
这部分参考以下链接,大家可以看下大神的解说。
自动引用计数(Automatic Reference Count,简称ARC),是苹果在WWDC(Worldwide Developers Conference)2011年大会上提出的用于内存管理的计数,所以11年以后接触iOS开发的可能无法理解前辈们使用MRC来管理内存的痛苦。即使是高级的iOS开发者也无法一次性的管理好内存。
有个面试经常问到的问题:使用ARC后,完全不用自己管理内存了吗?
如果对ARC了解不够或者一直以来依赖于ARC来管理内存的开发者来说可能回答的支支吾吾,那么面试官一下就能知道你的水平如何了。
【就算使用ARC后,仍然有两部分是需要我们手动管理内存的:block块的使用和core foundation的使用】这里不做详述
不管你是【前辈】还是【小白】,能够正确使用ARC和MRC,发挥ARC的作用,同时也要掌握MRC,这样才能完全管理好内存。
3.OC中的ARC
如果是创建简单的Command Line tool 选择OC语言,在project 中的Build Settings 里面设置 OC ARC为NO
如果是创建Single View Application工程,我们则需要修改下参数,如果在AppDelegate.m中使用MRC,如下设置:Targets–>Build Phases–>AppDeleagte 添加-fno-objc-arc 编译参数:
接下来以工程为例 来进一步了解MRC
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSObject *object = [[NSObject alloc]init];
NSLog( @" object.retainCount is %ld",[object retainCount]);
[object release];
NSLog( @" object.retainCount is %ld",[object retainCount]);
return YES;
}
两次输出的retainCount 都为1,可能有人觉得困惑了 ,应该是1、0. 首先在object对象内存已经被回收的情况下,我们不能再去使用object,这种做法本身就是错误的,其次在第二次打印object的引用计数时,[object release]中系统已经知道要回收object对象了,就没有必要将retainCount再去减一,一个对象被回收后,它所有的内存空间就会变得没有意义,而且不将这个值从1变成0,还减少了一次系统的操作。加速对象的回收
OC中常见的ARC问题还有循环引用
对象A和对象B相互引用对方作为自己的成员变量,只有依赖对方才能将自己销毁,这样导致了循环引用的问题。解决办法:
- 自己主动断开引用
- 使用弱引用(引用对象,但是不使对象的成员变量加一,常见在delegate模式中).ARC:一端使用strong,一端使用weak、MRC:一端使用assign,一端使用retain
补充:黄金法则
凡是alloc.retain,copy,mutableCopy就要对应使用release或者autorelease。
希望大家能多提意见,有问题和有疑问的地方在下面评论。