捋捋ARC

来源:互联网 发布:各种淘宝助理版本下载 编辑:程序博客网 时间:2024/06/04 08:20

定义:wiki

捋一捋ARC,或者说看看iOS的内存管理机制

引用计数:

引用计数是来用帮助oc进行内存回收的机制,如上图表示的代码类似于

NSObject *p1 = [NSObject new];NSObject *p2 = p1;NSObject *p3 = p1
指针指向了同一块内存区域,即对象存放的区域,用引用计数来描述的话,此刻对象的引用计数为3。

在引用计数为0时该内存区域将被回收用于存储其他。

MRC(manual reference counting)/MRR(manual retain-release):手动引用计数管理

既然是手动,说明引用计数不会像上面的代码一样NSObject *p2 = p1就让obj的引用计数加一。需要手动调用方法retain来增加引用计数

即 NSObject *p2 = [p1 retain];

相应的也不会在p2 = nil时减少引用计数,而需要调用release方法

即 [p2 release];

其他:

 autorelease:调用后,对象会在当前的autoreleasepool结束时将引用计数减1

对象delloc:在dealloc下需要将不再被使用的指针调用release。


那么在ARC的时代下这些方法是怎么做到对开发者不可见的呢

ARC下系统会在编译时期自动加入对应的retain、release

可以考虑以下场景:

1.无返回值的方法:

ARC下文章开头的代码实际上是如下形式:

NSObject *p1 = [NSObject new];NSObject *p2 = [p1 retain];NSObject *p3 = [p1 retain]//when no longer used[p3 release];[p2 release];[p1 release];

2.有返回值的方法:

ARC下:

- (NSString *)genSomeObj{    NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];    return a;}
MRC下:

- (NSString *)genSomeObj1{    NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];    return a;}- (NSString *)genSomeObj2{    NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];    return [a autorelease];}- (void)main{    NSString *s1 = [self genSomeObj1];    [s1 release];    NSString *s2 = [self genSomeObj2];    //由于autorelease,不需要调用[s2 release]}


ARC有一个规定:以new、alloc、copy、mutableCopy开头的方法需要由调用者主动管理内存

即同样是产生一个string的方法genSomeObj和newSomeObj,在ARC下被系统补充后的实现如下:

- (NSString *)genSomeObj{    NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];    return [a autorelease];}- (NSString *)newSomeObj{    NSString *a = [[NSString alloc]initWithFormat:@"%@",@"ARC"];    return a;}

调用newSomeObj方法的地方需要生成的指针不再使用时调用一次release(当然也是系统去调)

由于genSomeObj调用了autorelease,假设调用生成的指针还要继续指向该对象(需要保留该对象),那么还需要在此retain,例如

- (void)main{    NSString *b = [[self genSomeObj]retain];    //do something with b}...{...   [b release];...}

3.dealloc

ARC的dealloc将调用实例变量的release(不同于MRC下开发可以随时release,所以MRC下之需要开发自己写未被release的对象即可)

4.ARC下提供的property:

property的目的是为实例变量提供了更方便的访问方式,系统会为声明为property的obj生成实例变量_obj,并且提供默认的getter\setter

假设是MRC的环境,setter会是怎样的呢?

- (void)setObj:(NSObject *)obj{    [_obj release];    [obj retain];    _obj = obj;}

为什么会这样?

1.实例变量_obj可能会有旧值,此时它需要指向新的对象地址的,所以需要将之前对象的引用计数减1(如果_obj之前指向了某个对象)

2.新对象马上要被_obj引用,所以需要obj增加引用计数

3.开始引用(赋值操作)


property是可以有好几种形容词的,说下最常见的nonatomic,strong,weak,assgin

nonatomic:字面意思表示对象操作不具有原子性,如果不加这个形容词,那么对象的访问会被加锁

strong:属性赋值时会自动retain

weak:弱引用不会增加引用计数、会在指向的对象将要销毁时自动置为nil,防止也指针,具体可见weak的详细介绍

assgin:一般用于修饰基本类型,如果修饰对象,是不会自动进行retain操作的(不加引用计数)

5.block的使用

block是c/c++里闭包的实现,关于闭包的理解见阮一峰的blog

block分为NSStackBlock\NSGlobalBlock\NSMallocBlock:栈block,全局block,堆bock,其中栈block存储于栈,其他存储于堆

一个block在创建时属于stackblock还是globalblock,取决于block内是否使用了外部变量,在没有使用外部变量时为globalblock,有globablock的好处是对于它的很多方法都不需要实际处理,例如copy操作。

但在ARC下其实很少会使用stackblock,只要将新创建的block赋值指针,ARC下会自动进行copy操作,stackblock即化身为mallocblock。

MRC下入若执行以下代码

@interface MRCObj(){    void(^block)();}@end@implementation MRCObj- (void)test{    NSInteger i= 0;    block = ^(){__unused NSInteger b = i;};}- (void)testBlock{    block();}@end//somewhere{    MRCObj *mo = [MRCObj new];    [mo test];    [mo testBlock];}
会由于block变量指向了一个stackblock,而方法结束后stackblock释放,再次调用则引起崩溃。

所以在MRC下则需要主动进行Block_copy、Block_release操作例如,ARC下则如上所述会自行拷贝

Tips:

亲自测试MRC运行情况可在Build Phase的Compile Sources内将该文件设为-fno-objc-arc

0 0
原创粉丝点击