ARC的修饰符

在ARC有效的环境下,其内存管理规则和手动计数相同

  • 自己生成对象,自己持有
  • 非自己生成对象,自己也能持有
  • 自己持有的对象不再需要时释放
  • 非自己持有的对象无法释放

所有权修饰符

OC中的变量类型有对象类型(即指向OC类的指针NSObject *)和id类型(用于隐藏对象类型类名部分,相当于C语言中的void *)

在ARC有效时,id类型和对象类型与C语言的其他类型不同,必须加上所有权修饰符

  • __strong 修饰符
  • __weak修饰符
  • __unsafe __unretained修饰符
  • __autoreleasing修饰符

__strong修饰符

__strong修饰符是id类型和对象类型的默认修饰符,即

id obj = [[NSObject alloc] init];
//等效于
id __strong obj = [[NSObject alloc] init];
//或者
__strong id obj = [[NSObject alloc] init];

__strong修饰符的内存管理:

  1. __strong修饰符的变量赋值对象,即可完成对自己生成或非自己生成对象的强引用
  2. 当变量超出作用域被废弃、变量所属对象废弃(赋值nil)或者变量赋值即会释放对原有对象的强引用,相当于对对象执行release操作

而当对象没有了所有者 就会被销毁

示例如下:

//1.
{
    //取得非自己生成并持有的对象
    id __strong obj = [NSMutableArray array];
    //因为obj为强引用 所以自己持有对象
}
//此时变量obj超出作用域被废弃时,强引用失效,所以自动释放自己持有的对象

//2.
{
    //取得自己生成并持有的对象
    id __strong obj = [[NSObject alloc] init];
    //因为obj为强引用 所以自己持有对象
}
/*
* 此时变量obj超出作用域,强引用失效,所以自动释放自己持有的对象
* 对象所有者不存在,因此废弃该对象
*/ 

//3. 

id __strong obj0 = [[NSObject alloc] init];
//obj0强引用对象A
id __strong obj1 = [[NSObject alloc] init];
//obj1强引用对象B
obj0 = 0bj1;
//obj0强引用对象B,此时对对象A强引用失效,对象A没有所有者因此废弃对象A
obj0 = nil;
//因为obj0被赋值nil,因此其对对象B的强引用失效 此时对象B的强引用只剩下obj1

OC的类成员变量和方法参数上也可以附有__strong修饰符的变量

@interface Test: NSObject 
{
    id __strong obj_;
}

- (void)setObject:(id __strong)obj;

@implementation Test
...
@end

注意:

__ strong、__weak、__autoreleasing修饰符,会将修饰符修饰的变量自动初始化为nil

__weak修饰符

主要用来避免使用强引用导致的循环引用

循环引用很容易发生内存泄漏,内存泄漏就是应当废弃的对象在吃啊出其生存周期后继续存在

__weak提供弱引用,并不持有对象实例

错误使用:

id __weak obj = [[NSObject alloc] init]; //编译器会报警高
//这种情况下 obj并不持有生成的对象,而是持有对象的弱引用,所以对象会立即释放

正确使用

{
    id __strong obj = [[NSObject alloc] init];
    id __weak obj1 = obj;
}

__weak修饰符修饰的变量当持有某个对象的弱引用时,若该对象被废弃(没有了持有者),则弱引用自动失效并处于值为nil状态

__ unsafe__unretained修饰符

是不安全的修饰符。因为__weak只能用在iOS5之上的版本,所以在iOS4就只能使用__unsafe__retained修饰符

__unsafe__unretained变量修饰的对象不属于编译器的内存管理对象

//__unsafe__retained与__weak类似,并不持有生成对象所以对象会立即释放
id __weak obj = [[NSObject alloc] init]; //编译器会报警高

unsaferetained变量修饰的对象当销毁时不会自动置nil,因此有可能访问悬空指针

因此,不要使用这个修饰符

__autoreleasing修饰符

在arc有效的情况下 即将autorelease方法变为``

//非ARC环境下
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init]; 
[obj autorelease];
[pool drain];
//ARC环境下
@autoreleasepool {
    id __autoreleasing obj = [[NSObject alloc] init];
}

注意

@autoreleasepool {}在非ARC环境下也能使用 推荐@autoreleasepool来表示自动释放池,提高程序的可读性

就像我们几乎不显式的写__strong修饰符一样 我们也几乎不显式的附加__autoreleasing修饰符。编译器会自动帮我们添加__autoreleasing修饰符,例如以下情况:

  1. 编译器会自动将返回值注册到autorelease pool

    编译器会检查方法名,如果使用alloc/new/copy/mutableCopy以外开头的方法,则自动将返回值注册到`autorelease pool中

    + (id)array {
    return [[NSMutableArray alloc] init]
    }
    //等效于
    + (id)array {
    id __strong obj = [[NSMutableArray alloc] init];
    return obj;
    }

    obj由于return会使对象变量超出作用域,所以自己持有的强引用被释放,但该函数以对象作为返回值,编译器会自动帮其注册到autorelease pool中

  2. 访问__weak修饰的变量一定要访问注册的到autoreleapool的对象

    因为__weak只持有对象弱引用,在访问过程中,对象有可能销毁,而把对象注册到autorelease pool就能保证对象在访问结束前不会被销毁

    id __weak obj0 = obj1;
    NSLog(@"class = %@", [obj0 class]);
    //等效于
    id __weak obj0 = obj1;
    id __autoreleasing tmp = obj1;
    NSLog(@"class = %@", [tmp class]);
  3. id的指针或者对象指针会默认加上__autoreleasing修饰符
    id的指针id *objNSObject **obj在没有显式指定时会被附加上__autoreleasing修饰符

    例如:

     - (nullable instancetype)dataWithContentsOfFile:(NSString *)path ... error:(NSError **)errorPtr;
    //相当于
    - (nullable instancetype)dataWithContentsOfFile:(NSString *)path... error:(NSError * __autoreleasing *)errorPtr;

    使用__autoreleasing修饰的变量作为对象取得参数就和用非自己生成对象方法取得返回值的对象完全一样,会注册到autorelease pool

    //源码应该类似这个样子
    - (nullable instancetype)dataWithContentsOfFile:(NSString *)path... error:(NSError * __autoreleasing *)errorPtr {
    *error = [[NSError alloc] initWithDomain:MyAppDomin code:errorCode userInfo:nil];
    }

    在使用过程中应该注意,保持赋值互相对象的修饰符一致:

    NSError * __autoreleasing error;//修改修饰符为__autoreleasing保持一致进行赋值
    NSError **pError = &error;
    //或者
    NSError * error;
    NSError * __strong *pError = &error;

    在下面的NSError **参数赋值了一个__strong对象变量也没报错,是因为编译器自动帮我们做了转换

    NSError * error;
        id obj = [NSData dataWithContentsOfFile:@"" options:0 error:&error];
    //编译器帮我们转化为了
    NSError __strong *error;
    NSError __autoreleasing *tmp = error;
    id obj = [NSData dataWithContentsOfFile:@"" options:0 error:&tmp];