面试题答案
一键面试循环引用的产生
在Objective-C中,当两个或多个对象相互强引用对方时,就会产生循环引用。例如,假设有两个类 ClassA
和 ClassB
:
@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
和 b
相互强引用,它们的引用计数都不会降为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
修饰的 weakSelf
,SubView
对 CustomView
的引用变为弱引用,从而避免了循环引用。
多层数据模型关系
假设有三个类 ParentModel
、ChildModel
和 GrandChildModel
:
@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
通过将 GrandChildModel
对 ChildModel
的引用和 ChildModel
对 ParentModel
的引用设置为弱引用,打破了可能出现的循环引用,确保对象在不再被需要时能够正常释放。