MST
星途 面试题库

面试题:Objective-C属性内存管理之循环引用场景及解决办法

请举例说明在Objective-C中,使用属性时可能会出现循环引用的场景,并阐述至少两种解决循环引用问题的方法及原理。
47.6万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

循环引用场景举例

在Objective-C中,当两个对象相互持有对方的强引用属性时,就会出现循环引用。例如:

@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *classB;
@end

@interface ClassB : NSObject
@property (nonatomic, strong) ClassA *classA;
@end

假设在某个地方创建了这两个类的实例,并相互赋值:

ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.classB = b;
b.classA = a;

此时a强引用bb又强引用a,即使ab超出作用域,由于它们相互持有对方,引用计数不会降为0,导致内存泄漏。

解决循环引用的方法及原理

  1. 使用弱引用(weak 原理:弱引用不会增加对象的引用计数。当对象的引用计数降为0并被销毁时,指向该对象的所有弱引用会自动被设置为nil。 在上述例子中,修改ClassBclassA属性为弱引用:
@interface ClassB : NSObject
@property (nonatomic, weak) ClassA *classA;
@end

这样,ba的引用不会增加a的引用计数,当a的其他强引用都被释放后,a的引用计数降为0并被销毁,此时b.classA自动变为nil,从而打破循环引用。

  1. 使用无主引用(unowned 原理:无主引用同样不会增加对象的引用计数。与弱引用不同的是,当被引用的对象销毁后,指向它的无主引用不会自动被设置为nil,如果此时访问无主引用指向的已销毁对象,会导致程序崩溃。所以使用无主引用时要确保被引用对象的生命周期长于引用它的对象。 在上述例子中,修改ClassBclassA属性为无主引用:
@interface ClassB : NSObject
@property (nonatomic, unowned) ClassA *classA;
@end

这种方法适用于能确保ClassA实例的生命周期比ClassB实例长的场景,同样可以打破循环引用。