MST

星途 面试题库

面试题:Objective-C中Block的内存管理及循环引用问题

在Objective-C中,Block的内存管理有什么特点?请详细说明Block引起循环引用的原理,并给出至少两种解决循环引用的方法,同时结合代码示例阐述。
48.3万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

Block的内存管理特点

  1. 自动变量捕获:Block会捕获其定义时所在作用域中的自动变量(局部变量),并将这些变量的值拷贝到Block内部。如果捕获的是对象类型,默认情况下是强引用。
  2. 存储类型:Block有三种存储类型,分别是NSStackBlock(栈上)、NSMallocBlock(堆上)和NSGlobalBlock(全局区)。
    • NSStackBlock:当Block在栈上时,其生命周期与定义它的函数相同。函数返回后,Block将被销毁。这种类型的Block通常在函数内部定义且未被拷贝到堆上时存在。
    • NSMallocBlock:当Block被拷贝到堆上时(例如通过copy方法),它的生命周期由引用计数管理。可以像管理其他堆上对象一样,通过retainreleaseautorelease来控制其引用计数。
    • NSGlobalBlock:如果Block不捕获任何自动变量,它会被存储在全局区,生命周期与程序相同,不需要手动管理内存。

Block引起循环引用的原理

当一个对象(假设为objA)持有一个Block,而这个Block又捕获了objA本身(形成对objA的强引用),就会形成循环引用。因为objA持有Block,所以Block不会被释放;而Block又强引用objA,导致objA也不会被释放,从而造成内存泄漏。

解决循环引用的方法

  1. 使用__weak关键字(ARC环境下)
    • 原理__weak修饰的变量是弱引用,不会增加对象的引用计数。当对象被释放时,指向它的__weak变量会自动被设置为nil,从而打破循环引用。
    • 代码示例
@interface ViewController ()
@property (nonatomic, strong) void (^block)(void);
@property (nonatomic, strong) NSObject *obj;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf.obj);
    };
    self.obj = [[NSObject alloc] init];
}
@end
  1. 使用__block关键字(MRC环境下,ARC下使用__block可能仍会导致循环引用,不推荐)
    • 原理:在MRC环境下,__block修饰的变量不会被Block强引用。当对象被释放时,Block内对该变量的引用会失效,从而打破循环引用。
    • 代码示例
@interface ViewController ()
@property (nonatomic, strong) void (^block)(void);
@property (nonatomic, strong) NSObject *obj;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __block ViewController *blockSelf = self;
    self.block = ^{
        NSLog(@"%@", blockSelf.obj);
        blockSelf = nil; // 手动打破引用
    };
    self.obj = [[NSObject alloc] init];
}
@end
  1. 在Block执行完毕后手动打破引用
    • 原理:在Block执行完成后,将对象对Block的引用设置为nil,从而打破循环引用。
    • 代码示例
@interface ViewController ()
@property (nonatomic, strong) void (^block)(void);
@property (nonatomic, strong) NSObject *obj;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf.obj);
        weakSelf.block = nil;
    };
    self.obj = [[NSObject alloc] init];
    self.block();
}
@end