Block的内存管理
- Block的存储类型
- NSStackBlock:栈上的Block,当函数返回时,栈上的Block会被销毁。例如:
void testStackBlock() {
int a = 10;
void (^stackBlock)() = ^{
NSLog(@"%d", a);
};
stackBlock();
}
- NSMallocBlock:堆上的Block,需要手动管理内存。一般通过
copy
操作将栈上的Block复制到堆上。例如:
void testMallocBlock() {
int a = 10;
void (^stackBlock)() = ^{
NSLog(@"%d", a);
};
void (^heapBlock)() = [stackBlock copy];
// 使用完后,如果是ARC环境,ARC会自动释放,如果是MRC环境,需要手动释放
// 在MRC下:[heapBlock release];
}
- NSGlobalBlock:全局区的Block,当Block中不捕获任何外部变量时,会被存储在全局区,不需要进行内存管理。例如:
void (^globalBlock)() = ^{
NSLog(@"Global Block");
};
- ARC下的内存管理
- 在ARC环境下,编译器会自动对Block进行
retain
和release
操作。当Block被捕获到对象的属性或者作为函数返回值时,会自动retain
。例如:
@interface MyClass : NSObject
@property (nonatomic, copy) void (^blockProperty)();
@end
@implementation MyClass
- (void)setUpBlock {
int a = 10;
self.blockProperty = ^{
NSLog(@"%d", a);
};
}
@end
- 当对象被释放时,其属性中的Block会自动
release
。
- MRC下的内存管理
- 在MRC环境下,需要手动对Block进行
retain
和release
操作。如果从栈上复制Block到堆上,使用copy
方法,这相当于retain
操作。例如:
@interface MyClass : NSObject
@property (nonatomic, copy) void (^blockProperty)();
@end
@implementation MyClass
- (void)setUpBlock {
int a = 10;
void (^stackBlock)() = ^{
NSLog(@"%d", a);
};
self.blockProperty = [stackBlock copy];
// 最后需要在合适的地方手动释放
// [self.blockProperty release];
}
@end
内存泄漏情况
- 循环引用导致内存泄漏
- 当Block捕获对象并持有对象,而对象又持有该Block时,会产生循环引用导致内存泄漏。例如:
@interface MyClass : NSObject
@property (nonatomic, copy) void (^blockProperty)();
@end
@implementation MyClass
- (void)dealloc {
NSLog(@"MyClass dealloc");
[super dealloc];
}
- (void)setUpBlock {
self.blockProperty = ^{
NSLog(@"Inside block, self = %@", self);
};
}
@end
int main() {
MyClass *obj = [[MyClass alloc] init];
[obj setUpBlock];
[obj release];
return 0;
}
- 在上述代码中,
blockProperty
捕获了self
,self
又持有blockProperty
,导致循环引用,MyClass
对象无法被释放。
- 解决方法是使用
__weak
(ARC)或__unsafe_unretained
(MRC)修饰符来打破循环引用。例如在ARC下:
@interface MyClass : NSObject
@property (nonatomic, copy) void (^blockProperty)();
@end
@implementation MyClass
- (void)dealloc {
NSLog(@"MyClass dealloc");
}
- (void)setUpBlock {
__weak typeof(self) weakSelf = self;
self.blockProperty = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
NSLog(@"Inside block, self = %@", strongSelf);
}
};
}
@end
Block对外部变量的捕获机制
- 自动变量(局部变量)的捕获
- Block会捕获其定义时外部作用域的自动变量的值。例如:
void testCapture() {
int a = 10;
void (^block)() = ^{
NSLog(@"%d", a);
};
a = 20;
block();
}
- 上述代码输出
10
,因为Block捕获的是定义时a
的值。
- __block修饰的自动变量的捕获
- 使用
__block
修饰的自动变量,Block捕获的是变量的引用,而不是值。例如:
void testBlockVariable() {
__block int a = 10;
void (^block)() = ^{
a = 20;
};
block();
NSLog(@"%d", a);
}
- 上述代码输出
20
,因为Block捕获的是a
的引用,可以在Block内部修改其值。
- 静态变量和全局变量的捕获
- Block对静态变量和全局变量的捕获与普通访问一样,捕获的是变量的引用。例如:
static int staticVar = 10;
void testStaticAndGlobal() {
void (^block)() = ^{
staticVar = 20;
};
block();
NSLog(@"%d", staticVar);
}
- 上述代码输出
20
,Block可以直接修改静态变量的值。对于全局变量同理。