面试题答案
一键面试避免和解决内存循环引用的思路
- 设计模式方面:
- 代理模式:可以使用代理模式替代部分Block的使用。例如,在多层嵌套视图结构中,当某个层级的类需要通知上级类某个事件时,可以通过代理方法来实现。这样,上级类持有代理对象(通常是弱引用),而不是通过Block持有可能导致循环引用的对象。
- 回调函数(非Block形式):对于一些简单的通知场景,可以使用传统的函数指针回调。这样可以避免Block在捕获对象时可能带来的循环引用问题。
- 内存管理机制方面:
- 弱引用:在Block中对可能导致循环引用的对象使用弱引用。例如在Objective - C中,可以使用
__weak
关键字修饰对象。在Swift中,可以使用weak
关键字。这样,Block捕获对象时不会增加对象的引用计数,从而避免循环引用。 - 自动释放池:合理使用自动释放池,在适当的时机释放临时对象,减少内存占用,也有助于避免因内存管理不当导致的潜在循环引用问题。
- 弱引用:在Block中对可能导致循环引用的对象使用弱引用。例如在Objective - C中,可以使用
- 代码结构方面:
- 解耦代码:尽量减少不必要的对象间强引用关系。将复杂的逻辑拆分到不同的类或模块中,使得对象之间的依赖关系更加清晰和简单,从而降低循环引用发生的可能性。
- 合理的生命周期管理:确保对象在其生命周期结束时,相关的引用能够被正确释放。例如,在视图控制器的
dealloc
方法中,取消注册可能存在的Block回调等操作。
完整可运行的代码示例(以Objective - C为例)
#import <UIKit/UIKit.h>
// 最内层的视图类
@interface InnerView : UIView
@property (nonatomic, copy) void (^innerBlock)(void);
@end
@implementation InnerView
- (void)dealloc {
NSLog(@"InnerView dealloc");
}
@end
// 中间层的视图类
@interface MiddleView : UIView
@property (nonatomic, copy) void (^middleBlock)(void);
@end
@implementation MiddleView
- (void)dealloc {
NSLog(@"MiddleView dealloc");
}
@end
// 最外层的视图控制器类
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
InnerView *innerView = [[InnerView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
MiddleView *middleView = [[MiddleView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
[middleView addSubview:innerView];
[self.view addSubview:middleView];
__weak typeof(self) weakSelf = self;
innerView.innerBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
NSLog(@"Inner block accessed view controller: %@", strongSelf);
}
};
__weak typeof(middleView) weakMiddleView = middleView;
middleView.middleBlock = ^{
__strong typeof(weakMiddleView) strongMiddleView = weakMiddleView;
if (strongMiddleView) {
NSLog(@"Middle block accessed middle view: %@", strongMiddleView);
}
};
}
- (void)dealloc {
NSLog(@"ViewController dealloc");
}
@end
不同运行时环境(如iOS不同版本)下可能需要注意的差异
- iOS 4.3及之前:在iOS 4.3及之前的版本中,ARC(自动引用计数)还未引入。需要手动管理内存,在处理Block和对象引用时,要特别注意
retain
、release
和autorelease
的正确使用,以避免内存泄漏和循环引用。 - iOS 5.0及之后:ARC开始引入,大大简化了内存管理。但在使用Block捕获对象时,仍需注意
__weak
(iOS 5.0开始支持)和__unsafe_unretained
(iOS 5.0开始支持,不推荐使用,因为对象释放后指针不会自动置为nil
)的正确使用,确保避免循环引用。同时,不同iOS版本在内存管理机制的优化上可能存在差异,在性能敏感的场景下,需要注意测试和优化。例如,iOS 9及之后在内存管理和对象释放的效率上有一定的优化。
在Swift中,同样要注意weak
和unowned
(类似__unsafe_unretained
,但在对象释放时会导致运行时错误)的使用,不同版本Swift对内存管理和闭包(类似Block)的优化也可能会影响代码的性能和循环引用处理。