面试题答案
一键面试可能出现循环引用的场景举例
在ARC(自动引用计数)环境下,一个常见的循环引用场景是两个对象相互持有对方的强引用。例如:
@interface ClassA : NSObject
@property (strong, nonatomic) ClassB *classB;
@end
@interface ClassB : NSObject
@property (strong, nonatomic) ClassA *classA;
@end
@implementation ClassA
@end
@implementation ClassB
@end
在上述代码中,如果创建 ClassA
和 ClassB
的实例,并相互赋值对方的属性:
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.classB = b;
b.classA = a;
此时 a
对 b
有一个强引用,b
对 a
也有一个强引用,形成了循环引用。当超出作用域时,由于双方的强引用导致对方的引用计数不会降为0,从而无法被释放。
解决循环引用的方法及其原理
1. 使用弱引用(weak
)
- 方法:将其中一个对象的属性改为
weak
类型。例如,修改ClassB
的classA
属性为weak
:
@interface ClassB : NSObject
@property (weak, nonatomic) ClassA *classA;
@end
- 原理:
weak
引用不会增加对象的引用计数。当对象的强引用计数变为0被释放时,指向该对象的所有weak
引用会自动被设置为nil
。在上述例子中,ClassA
对ClassB
是强引用,ClassB
对ClassA
是弱引用。当ClassA
的其他强引用都消失后,其引用计数变为0被释放,此时ClassB
的classA
属性自动变为nil
,从而打破了循环引用。
2. 使用无主引用(unowned
)
- 方法:在某些情况下,比如确定两个对象的生命周期相同且相互依赖,可以使用
unowned
引用。例如,在Swift中:
class ClassA {
var classB: ClassB?
init() {
classB = ClassB(a: self)
}
}
class ClassB {
unowned let classA: ClassA
init(a: ClassA) {
classA = a
}
}
- 原理:
unowned
引用和weak
类似,不会增加对象的引用计数。不同的是,unowned
引用在对象被释放后不会被设置为nil
,因此使用unowned
时要确保引用的对象在其生命周期内始终存在,否则会导致野指针错误。在上述例子中,ClassA
持有ClassB
的强引用,ClassB
持有ClassA
的unowned
引用,由于ClassA
先初始化ClassB
,且二者生命周期相关,所以可以使用unowned
打破循环引用。