美文网首页
ARC规则-所有权修饰符

ARC规则-所有权修饰符

作者: 关灯侠 | 来源:发表于2018-09-05 23:51 被阅读16次

所有内容引用自《Objective-C 高级编程 iOS与OS X多线程和内存管理》,加入了自己的部分理解。

本节小结,点小1跳到底部[1]


Objective-C处理对象,可将变量类型定义为id类型和对象类型。

ARC有效时,必须在类型上附加所有权修饰符。共4种:

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing
1、__strong

__strongid类型和对象类型默认的所有权修饰符。

id obj = [[NSObject alloc] init];
// 等价于
id __strong obj = [[NSObject alloc] init];

__strong有什么作用?对比一下arc非arc就知道了。

// 非arc的实现
{
    id obj = [[NSObject alloc] init];
    [obj release];
}

// 等价于,arc的实现
{
    // 变量强引用持有对象
    id __strong obj = [[NSObject alloc] init];
}
// 作用域结束,变量废弃,强引用失效,对象会被释放

__strong修饰的变量在超出变量作用域时,变量会被废弃,没有谁引用对象,对象也会被释放。

除了自己生成的对象自己持有非自己生成的对象自己也可以持有也是适用的。

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

其他,在赋值成员变量方法参数等,都是可以正确管理对象的所有者。

另外,附有修饰符的自动变量初始化都为nil

id __strong obj0;
id __weak obj1;
id __autoreleasing obj2;

// 等价于
id __strong obj0 = nil;
id __weak obj1 = nil;
id __autoreleasing obj2 = nil;

__strong修饰符的意义,就是不必再考虑retainrelease,完美地满足引用计数式内存管理的思考方式

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

前面两条前面有验证,作用域结束,废弃带有__strong修饰符的变量,对应的对象也释放了,满足了自己持有的对象,不再需要时释放

2、__weak

常用的用法,解决循环引用

🌰一个循环引用:

// child是Test的strong修饰的属性
{
    // test0 持有对象A的强引用
    id test0 = [[Test alloc] init]; //对象A
    // test1 持有对象B的强引用
    id test1 = [[Test alloc] init]; //对象B

    // 对象A的属性child持有对象B的强引用
    // 那么现在持有对象B的强引用变量有对象A的child、test1
    test0.child = test1;

    // 对象B的属性child持有对象A的强引用
    // 那么现在持有对象A的强引用变量有对象B的child、test2
    test1.child = test0;
}
// test0超出作用域,强引用消失,自动释放对象A

// test1超出作用域,强引用消失,自动释放对象B

// 此时持有对象A的是对象B的child,持有对象B的是对象A的child

// 发生内存泄露

另一种循环,是自身循环,🌰

id test = [[Test alloc] init];
test.child = test;

通常遇到循环的时候,我们立马能想到__weak

__weak提供弱引用,不持有对象,如果生成的对象赋值给__weak变量会立即被释放。编译器会有 警告。

// 立即被释放,会提示警告
id __weak obj = [[NSObject alloc] init];

可以改为:

id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;

__weak还有一个优点,持有某对象的弱引用时,当对象被废弃,弱引用会自动失效,并置为nil

id __weak obj1 = nil;
{
    id __strong obj0 = [[NSObject alloc] init];
    obj1 = obj0;
    NSLog(@"A: %@",obj0);
}
NSLog(@"B: %@",obj1);

// A: 肯定有地址
// B: nil

所以,可以通过检查附有__weak修饰的变量是否为nil,来判断对象是否被释放。

__weak只能用在iOS5以上OS X Lion,低版本只能使用__unsafe_unretained修饰。

3、__unsafe_unretained

__unsafe_unretained是不安全的所有权修饰符。尽管ARC的内存管理是编译器的工作。但是__unsafe_unretained修饰的变量不属于编译器的内存管理的对象。

__unsafe_unretained作用和__weak一样,不持有对象。在使用__unsafe_unretained修饰的对象时,一定要确保对象存在,否则会引起崩溃。

4、__autoreleasing

ARC有效时,不能直接使用autoreleaseNSAutoreleasePool

// 非ARC的情况
NSAutoreleasePool *pool = [[NSAutoreleasePool  alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];

// 等价于ARC的情况
@autoreleasepool{
    id __autoreleasing obj = [[NSObject alloc] init];
}

可以看出@autoreleasepool块来替代NSAutoreleaPool类对象生成、持有以及废弃
__autoreleasing代替了autorelease,注册到autoreleasepool

:编译器检查到不是以alloc/new/copy/mutableCopy开始的方法,都自动将返回值注册到autoreleasepool

@autoreleasepool{
    // 取得非自己生成并持有的对象
    // 判断方法名,自动注册到autoreleasepool
    id __strong obj = [NSMutableArray array];
}
// obj超出作用域,强引用失效,对象释放
// @autoreleasepool代码块结束,注册到autoreleasepool的所有对象都释放

__weak情况有些不同,实际上__weak修饰的变量,也一定注册到了autoreleasepool。因为__weak只持有对象弱引用,在访问对象的过程中,有可能被释放,注册到autoreleasepool中,就能保证@autoreleasepool代码块结束前都存在。

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

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

最后一种非显示使用__autoreleasing情况,就是id的指针或对象的指针在没有指定的情况下,都会默认加上__autoreleasing

举个🌰,常用的方法参数中传递的NSError对象的指针

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

// 等价于
- (BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

非自己生成的对象也可以持有一样,作为__autoreleasing修饰的参数也会注册到autoreleasepool

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

// 编译会报错,
NSError *error = nil;
NSError **pError = &error;

// 必须要加上__strong修饰符
NSError *error = nil;
NSError * __strong *pError = &error;
//
NSError __weak *error = nil;
NSError * __weak *pError = &error;

一个隐式转换。

NSError __strong *error = nil;
// 前面知道performOperationWithError参数需要一个__autoreleasing类型
BOOL result = [obj performOperationWithError:&error];

// 按照指针类型一致的原则,应该会出错,但实际没有
// 等价于
NSError __strong *error = nil;
NSError __autoreleasing *tmp = error;
BOOL result = [obj performOperationWithError:&tmp];

小结

1、__strongid类型和对象类型默认的所有权修饰符。
2、__strong修饰的变量超出作用域,强引用失效,对象释放,更符合内存管理思考方式
3、__weak弱引用,解决循环引用。对象被废弃后,弱引用失效,自动置nil
4、__weakiOS5以上OS X Lion适用,低版本只能使用__unsafe_unretained__unsafe_unretained修饰的不属于编译器的内存管理对象,还需要确保对象存在。
5、@autoreleasepool块可以管理NSAutoreleaPool类对象生成、持有以及废弃
6、只要不是以alloc/new/copy/mutableCopy开始的方法,都自动将返回值注册到autoreleasepool
7、__weak修饰的变量也一定注册到autoreleasepool,避免提前释放。
8、id的指针或对象的指针在没有指定的情况下,都会默认加上__autoreleasing
9、指针赋值的时候,所有权修饰符要保持一致
10、无论是ARC还是非ARC,都可以使用@autoreleasepool块管理内存。


  1. 😊假装是锚点的脚注

相关文章

网友评论

      本文标题:ARC规则-所有权修饰符

      本文链接:https://www.haomeiwen.com/subject/ozwmwftx.html