MST

星途 面试题库

面试题:Objective-C 中 Block 的内存管理问题

在 Objective-C 中,当一个对象持有 Block,而 Block 又捕获了该对象时,会发生什么情况?如何避免由此引发的内存泄漏问题?请结合代码示例详细阐述。
33.7万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

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 变量可能指向已释放内存的问题。