OC内存管理小计

不管在ARC还是MRC中,都遵循下面两个基本原则

You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”.

MRC

MRC的基本原则

OC中,在alloc、new、retain、copy、mutableCopy一个对象后

或者CF中,在Create(包含Create的函数)、CFRetain、Copy(包含Copy的函数)一个对象后

这个对象的引用记数会加1,由于这个引用记数是在当你加的,所以你有责任在不需要它的时后对它进行release操作。

这与总的基本原则也是相符的,你负责你自己用alloc, init, new, copy & mutableCopy创建的对象的生命周期。

如果不是用alloc, init, new, copy & mutableCopy创建的,这个对象就不需要由你来负责,MRC下一种实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* MRC,此段代码摘抄自[《Objective-C 高级编程》](https://book.douban.com/subject/24720270/)
*/
- (id)object
{
id obj = [[NSObject alloc] init];
/*
* 自己持有对象
*/
[obj autorelease];
/*
* 取得对象的存在,但自己不持有对象
*/
return obj;
}

如上,这个对象由autorelease pool来负责释放,不需要创建他的人负责释放。

MRC的AutoreleasePool

AutoreleasePool就是一个池子,给OC对象发送autorelease消息(CF对象对应的是CFAutorelease函数)后就把对象放到池子中,然后在池子结束的地方,对池子中的每一个对象都发送一次release消息。

ARC

ARC简单的理解就是编译器自动在代码合适的位置加入retain和release代码,背后没那么简单,这里也不深究了,ARC只支持OC对象。

ARC中,不能对OC对象进行retain、release、autorelease操作。

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

黑幕背后的Autorelease

在ARC中,虽然不可以使用retain、release、autorelease,但是可以使用

1
2
3
@autoreleasepool{
...
}

这样可以在进行大的内存操作时,手动的进行一些内存管理。

函数返回问题

这里只记录返回CF对象的情况,因为OC对象有ARC,应该也很少会遇到这个问题了,我也没去研究。

如下的函数

1
2
3
4
5
6
7
8
9
10
11
//OC
+ (CGColorSpaceRef)getAColorSpace {
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
return colorSpace;
}
//CF
CGColorSpaceRef getAColorSpace(){
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
return colorSpace;
}

如果我们运行Analyze,编辑器会给我们一个Potential leak of an object stored into ‘colorSpace’的提示。

原因就是我们调用了CF的Create函数生成了一个CF对象,当前代码片段不需要时又没有release它。

那可怎么办,总不能Create完又release,然后返回个空吧?

有三种办法避免这个

  • 第一种是修改函数名,可以改成如下的名字

    1
    2
    3
    4
    //OC
    + (CGColorSpaceRef)allocAColorSpace {...}
    //CF
    CGColorSpaceRef CreateAColorSpace(){...}

    这样,名字中包含alloc、Create,说明调用这个方法的代码持有这个方法返回的对象,对象的释放应该由外面的代码来管理。

    OC中包含new、alloc、copy的方法名

    CF中包含Create、Copy的方法名

    都代表返回的对象调用这个方法的代码来管理,如最开始所说的,我们再不需要这个实例时在调用CFRelease来释放。

  • 第二种是给colorSpace添加autorelease,像下面这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    + (CGColorSpaceRef)getAColorSpace {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CFAutorelease(colorSpace);
    return colorSpace;
    }
    CGColorSpaceRef getAColorSpace(){
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CFAutorelease(colorSpace);
    return colorSpace;
    }

    这样,就把colorSpace放入autorelease pool,把这个对象管理的权利交给autorelease pool,colorSpace不会马上释放,外面的代码也能用到它。

  • 第三种是将CF对象Toll-Free转换为OC对象,由ARC管理内存

方法命名与释放

另外,ARC中,创建实例的方法的命名会影响创建的对象的释放

新建实例的方法由alloc, init, new, copy & mutableCopy开头命名

对象有两种释放方式:

  • 没用引用了就直接释放
  • 调用Autorelease,由Autorelease pool释放

不同的方法前缀会有不同的效果,

详情可参见 ARC and releasing object created in method

Toll-Free

苹果官方文档

总结:

  • CF转化为OC时,并且对象的所有者发生改变,则使用CFBridgingRelease()__bridge_transfer
  • OC转化为CF时,并且对象的所有者发生改变,则使用CFBridgingRetain()__bridge_retained

当一个类型转化到另一种类型时,但是对象所有者没有发生改变,则使用__bridge.

http://www.beyondabel.com/blog/2014/03/05/mrc-arc/

1
2
3
4
CFArrayRef array;
...
NSArray *nArray = (__bridge_transfer NSArray *) array; //对象的所有者发生改变NSArray在ARC下会自动释放
//CFRelease(array); //这里不需要CFRelease了

与MRC混用时的疑惑

2017年07月26日记:

今天MRC与ARC混用,有些疑惑,MRC方法中初始化的实例,作为返回值传到ARC中,在ARC中需要怎么处理。

然后做了个实验

在MRC中新建TestMRCARC类,该类重载dealloc方法并输出log

然后在MRC随便某个类中添加如下方法

1
2
3
4
5
+ (TestMRCARC *)makeTestMRCARC {
TestMRCARC *s = [[TestMRCARC alloc] init];
// [s autorelease];
return s;
}

在ARC中调用该方法:

1
2
3
- (IBAction)btnClick:(id)sender {
TestMRCARC *s1 = [XXX makeTestMRCARC];
}

发现s1并没有被释放。

原因是MRC中的方法命名采用的是make开头,所以ARC中不会对s1进行管理。

解决方法:

  1. 将方法名改成newTestMRCARC,s1就会得到正确的释放
  2. 或将[s autorelease]取消注释,由ARC中的Autorelease pool来释放

另外,在ARC中,即使方法没用按照规则命名,不使用alloc、new、retain、copy、mutableCopy,变量也会得到正确的释放。但是我们再编码时,最好还是按照基本规则来命名。

参考

ARC下的autorelease

请用金钱尽情的践踏我