所有内容引用自
《Objective-C 高级编程 iOS与OS X多线程和内存管理》
,加入了自己的部分理解。
本节小结,点小1
跳到底部[1]
Objective-C
处理对象,可将变量类型定义为id
类型和对象类型。
ARC
有效时,必须在类型上附加所有权修饰符。共4种:
__strong
__weak
__unsafe_unretained
__autoreleasing
1、__strong
__strong
是id
类型和对象类型默认的所有权修饰符。
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
修饰符的意义,就是不必再考虑retain
或release
,完美地满足引用计数式内存管理的思考方式
:
- 自己生成的对象,自己持有
- 非自己生成的对象,自己也可以持有
- 自己持有的对象,不再需要时释放
- 非自己持有的对象,不能释放
前面两条前面有验证,作用域结束,废弃带有__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
有效时,不能直接使用autorelease
、NSAutoreleasePool
// 非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、__strong
是id
类型和对象类型默认的所有权修饰符。
2、__strong
修饰的变量超出作用域,强引用失效,对象释放,更符合内存管理思考方式
。
3、__weak
弱引用,解决循环引用。对象被废弃后,弱引用失效,自动置nil
。
4、__weak
在iOS5以上
和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
块管理内存。
-
😊假装是锚点的脚注 ↩
网友评论