所有内容引用自
《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块管理内存。
-
😊假装是锚点的脚注 ↩












网友评论