面试题答案
一键面试1. 会发生的情况
当一个对象持有 Block,而 Block 又捕获了该对象时,会形成循环引用。这是因为对象持有 Block,而 Block 内部又对该对象有强引用,导致双方相互持有,使得它们都无法被释放,从而产生内存泄漏。
2. 避免方法及代码示例
使用 __weak
关键字
在 ARC(自动引用计数)环境下,可以使用 __weak
关键字来解决循环引用问题。__weak
修饰的变量不会对对象产生强引用,当对象被释放时,__weak
修饰的变量会自动被设置为 nil
。
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.view.backgroundColor = [UIColor whiteColor];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
strongSelf.view.backgroundColor = [UIColor redColor];
}
});
}
@end
在上述代码中,首先使用 __weak
修饰了 self
,将其赋值给 weakSelf
。在 Block 内部,为了防止在执行 Block 过程中 self
被释放,又将 weakSelf
赋值给一个强引用的 strongSelf
,然后通过判断 strongSelf
是否为 nil
来确保对象仍然存在,进而执行相关操作。
在 MRC(手动引用计数)环境下使用 __unsafe_unretained
在 MRC 环境下,__weak
关键字不可用,可以使用 __unsafe_unretained
关键字。它和 __weak
类似,不会对对象产生强引用,但不同的是,当对象被释放时,__unsafe_unretained
修饰的变量不会被自动设置为 nil
,这可能会导致野指针错误。
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained id weakSelf = self;
self.view.backgroundColor = [UIColor whiteColor];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
id strongSelf = weakSelf;
if (strongSelf) {
[(ViewController *)strongSelf setViewBackgroundColor:[UIColor redColor]];
}
});
}
@end
同样先使用 __unsafe_unretained
修饰 self
,在 Block 内部通过强引用变量 strongSelf
并进行判断,来防止野指针访问。但需要特别注意对象释放后 __unsafe_unretained
变量可能指向已释放内存的问题。