MST

星途 面试题库

面试题:Objective-C中使用弱引用解决循环引用问题的原理与实践

描述在Objective-C开发中,循环引用是如何产生的,以及使用弱引用解决循环引用的具体原理。请结合实际代码示例,说明在一个复杂视图层级关系或者多层数据模型关系中,如何正确运用弱引用来避免循环引用导致的内存泄漏。
46.5万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

循环引用的产生

在Objective-C中,当两个或多个对象相互强引用对方时,就会产生循环引用。例如,假设有两个类 ClassAClassB

@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;

此时 ab 相互强引用,它们的引用计数都不会降为0,即使它们不再被其他对象所使用,也无法被释放,从而导致内存泄漏。

使用弱引用解决循环引用的原理

弱引用(weak)的特点是不会增加对象的引用计数。当对象的引用计数变为0并被释放时,指向该对象的所有弱引用都会自动被设置为 nil。这样就打破了循环引用,避免了内存泄漏。

实际代码示例 - 复杂视图层级关系

假设我们有一个自定义视图 CustomView,它包含一个子视图 SubView,而 SubView 中有一个按钮,按钮的点击事件处理需要访问 CustomView 的某些属性。如果直接使用强引用,就可能产生循环引用。

@interface SubView : UIView
@property (nonatomic, strong) id target;
@property (nonatomic, SEL) action;
- (void)setupButton;
@end

@implementation SubView
- (void)setupButton {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button addTarget:self.target action:self.action forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:button];
}
@end

@interface CustomView : UIView
@property (nonatomic, strong) SubView *subView;
- (void)buttonTapped;
@end

@implementation CustomView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.subView = [[SubView alloc] initWithFrame:self.bounds];
        // 使用弱引用来避免循环引用
        __weak typeof(self) weakSelf = self;
        self.subView.target = weakSelf;
        self.subView.action = @selector(buttonTapped);
        [self.subView setupButton];
        [self addSubview:self.subView];
    }
    return self;
}

- (void)buttonTapped {
    NSLog(@"Button in SubView was tapped.");
}
@end

在这个例子中,SubView 原本可能会强引用 CustomView,但通过使用 __weak 修饰的 weakSelfSubViewCustomView 的引用变为弱引用,从而避免了循环引用。

多层数据模型关系

假设有三个类 ParentModelChildModelGrandChildModel

@interface GrandChildModel : NSObject
@property (nonatomic, strong) ChildModel *childModel;
@end

@interface ChildModel : NSObject
@property (nonatomic, strong) ParentModel *parentModel;
@property (nonatomic, strong) GrandChildModel *grandChildModel;
@end

@interface ParentModel : NSObject
@property (nonatomic, strong) ChildModel *childModel;
@end

为了避免循环引用,可以这样修改:

@interface GrandChildModel : NSObject
@property (nonatomic, weak) ChildModel *childModel;
@end

@interface ChildModel : NSObject
@property (nonatomic, weak) ParentModel *parentModel;
@property (nonatomic, strong) GrandChildModel *grandChildModel;
@end

@interface ParentModel : NSObject
@property (nonatomic, strong) ChildModel *childModel;
@end

通过将 GrandChildModelChildModel 的引用和 ChildModelParentModel 的引用设置为弱引用,打破了可能出现的循环引用,确保对象在不再被需要时能够正常释放。