ARC的内存管理方式

2018-04-11  本文已影响0人  foreverSun_122

什么是ARC

ARC(自动引用计数)是一个编译期技术,介于垃圾回收(GC)和MRC之间,ARC让程序员不再需要书写retain/release/autorelease语句,runtime会维护一张引用计数表,编译器会在编译期在合适的位置自动给用添加retain/release/autorelease,它的特点是自动引用技术简化了内存管理的难度

ARC内存管理的思考方式:

ARC内存管理的思考方式与MRC一致。

ARC代码编写规则

  1. 不能在程序中定义和使用:retain、release、autorelease和retainCount
  2. 使用@autoreleasepool代替NSAutoreleasePool
  3. 不用在dealloc中释放实例变量(可以在dealloc中释放资源),也不需要调用[super dealloc]

所有权修饰符

id类型:用于隐藏对象类型的类名部分,类似于C语言中的void*;
ARC有效时,id类型和对象类型必须加上所有权修饰符:

__strong修饰符

__strong修饰符是默认的修饰符

//两种方式是等价的
id obj = [[NSObject alloc]init];
id __strong obj = [[NSObject alloc]init]

特性:strong表示强引用,持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。

  1. 与自己生成并持有对象
//ARC有效
{
    //自己生成,自己持有,obj为强引用,持有变量
    id __strong obj = [[NSObject alloc]init];
    //todo
    .......
}
//超出作用域,强引用失效

//以上代码在ARC无效下等价于
{
    id obj = [[NSObject alloc]init];
    //todo
    ......
    [obj release];
}

2、取得非自己生成但持有的对象

//取得非自己生成的对象,obj为强引用,所以持有该对象
id __strong obj = [NSArray array];

3、附有__strong修饰符的变量可以相互赋值

//obj0持有对象A的强引用
id __strong obj0 = [[NSObject alloc]init];

//obj1持有对象B的强引用
id __strong obj1 = [[NSObject alloc]init];

//obj2不持有任何对象
id __strong obj2 = nil;

/*
 * obj0持有obj1赋值的对象B的强引用,对象B的持有者为obj0、obj1
 * obj0被赋值,原先持有的对象A的强引用失效,对象A的所有者不存在,因此对象A废弃
 */
obj0 = obj1;

/*
 * obj2持有obj0赋值的对象B的强引用
 * 对象B的持有者为:obj0、obj1、obj2
 */
obj2 = obj0;

4、附有__strong修饰符的变量作为方法的参数也能正确的管理其对象的所有者

总结:通过__strong修饰符,不必再次键入retain或release,完美地满足了“引用计数式内存管理的思考方式”。

__weak修饰符

__weak修饰符,提供弱引用,弱引用不能持有实例对象,可以避免循环引用

/*
 * 编译器会给警告
 * obj为弱引用,并不持有对象,生成的对象会被立即释放,所以会给警告
 */
id __weak obj = [[NSObject alloc]init];

//obj0强引用持有对象,obj1弱引用不持有对象
id __strong obj0 = [[NSObject alloc]init];
id __weak obj1 = obj0;

注: 在持有某对象的弱引用时,若该对象被废弃,则弱引用自动失效,且处于nil被赋值的状态(空弱引用)

__unsafe_unretained修饰符

__unsafe_unretained是不安全的修饰符,修饰的变量不属于编译器的内存管理对象,同__weak修饰符一样,不能持有实例对象

/*
 * 编译器警告,obj不持有NSObject对象
 */
id __unsafe_unretained obj = [[NSObject alloc]init];

id __unsafe_unretained obj1 = nil;
{
    //obj0强引用NSObject对象
    id __strong obj0 = [[NSObject alloc]init];
    //obj1不持有NSObject对象
    obj1 = obj0;
    NSLog(@"A:%@",obj1);
}
/*
 * obj0变量超出作用域,强引用失效,释放持有变量
 * obj0指向的对象被废弃(悬垂指针),成为僵尸对象
 */
NSLog(@"B:%@",obj1);

执行结果:
A: <NSObject: 0x753e180>
B: <NSObject: 0x753e180>

最后一行NSlog,碰巧正常运行。因为obj0指向的对象已经被废弃,不能被访问

为什么要有__unsafe_unretained修饰符?

在iOS4以及OS X Snow Leopard的应用程序中,必须使用__unsafe_retained修饰符来代替__weak__unsafe_unretained修饰的对象赋值给__strong修饰符的变量时,必须保证被赋值对象确实存在。

__autoreleasing修饰符

ARC无效时

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSObject alloc]init];
[obj autorelease];
[pool drain];

ARC有效时,代码写为

//ARC指定“@autoreleasepool块”来替代“NSAutoReleasePool类对象生成、持有以及废弃”这一范围
@autoreleasepool {
    //添加__autoreleasing修饰符来代替调用autorelease方法
    id __autoreleasing obj = [[NSObject alloc]init];
}

非显示使用__autorelease的情况

__autoreleasing__strong一样通常都不会显示添加。

@autoreleasepool {
    // 变量为强引用,所以自己持有对象
    //但编译器判断其方法名后,自动将其注册到autoreleasepool
    id __strong obj = [NSMutableArray array];
}
+ (id)array
{
    //obj生成并持有对象,强引用
    id obj = [[NSMutableArray alloc]init];
    //强引用在obj超出作用域后会释放对象,但是该对象又作为返回值不能被释放,所以编译器会自动注册到autoreleasepool中
    return obj;
}

//得到的是注册到autoreleasepool中的对象
id obj = [NSMutableArray array];
id __weak obj1 = obj0;
NSLog(@"class = %@", [obj1 class])

//等价于

id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@", [tmp class]);

__weak修饰的对象为弱引用,在访问时可能会随时被废弃,只要把对象注册到autoreleasepool中,就能在@autoreleasepool块结束前确保对象存在

//id的指针
id *obj;
id __autoreleasing *obj;

//对象的指针
NSError **error;
NSError * __autoreleasing *error;

- (BOOL)performOperationWithError:(NSError **)error;

//error为对象指针,所以默认修饰符是__autoreleasing;
- (BOOL)performOperationWithError:(NSError *__autoreleasing *)error {
    *error = [[NSError alloc] initwithDomain:MyApplication code:errorCode userInfo:nil];
    return NO

}

这里- (BOOL)performOperationWithError:(NSError **)error;与除alloc/new/copy/mutableCopy外的其他非自己生成但持有的方法一样,{}里[[NSError alloc] initwithDomain:MyApplication code:errorCode userInfo:nil]生成的对象因为超出作用域后会释放对象,所以编译器会将其放入到autoreleasepool中,所以这里的alloc方法生成的对象(理论上是__strong修饰的对象)能够正常赋值给__autoreleasing修饰的*error

注:赋值给对象指针时,所有权修饰符必须一致

//默认是__strong
NSError *error = nil;
//默认是__autoreleasing
NSError **pError = &error;  //报错

NSError * __autoreleasing *pError1 = &error;    //不报错

//不报错
NSError _strong *error = nil;
BOOL result = [obj performOperationWithError:&error];

上面的error是__strong修饰的,但是performOperationWithError:里的参数是__autoreleasing修饰的,但是没有因修饰符不一致而报错,原因是编译器做了如下转换:

NSError __strong *error = nil;
NSError __autoreleasing *tmp = error;
BOOL result = [obj performOperationWithError:&tmp];

注:显示指定__autoreleasing时要注意,对象变了要为自动变量(包括局部变量、函数以及方法参数)

总结:

参考资料:

上一篇下一篇

猜你喜欢

热点阅读