面试题答案
一键面试Block的底层实现机制
数据结构
在Objective-C中,Block本质上是一个对象,其底层数据结构是一个struct
。这个结构体包含了一些与Block相关的信息:
- isa指针:用于表明该Block对象所属的类。所有对象都有
isa
指针,通过它可以找到对象的类信息。 - 调用函数指针:指向实际执行Block代码的函数。当Block被调用时,会通过这个指针找到具体的执行代码。
- 捕获变量相关信息:如果Block捕获了外部变量,结构体中会包含这些变量的相关信息,比如捕获变量的值或者对变量的引用。
例如,以下是一个简化的Block底层结构体示意(实际结构更复杂):
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
存储类型
- NSStackBlock:栈上的Block,生命周期与栈相关。当定义Block的函数返回时,栈上的Block会被销毁。这种类型的Block主要在函数内部定义且未进行拷贝操作时存在。例如:
void testFunction() {
int value = 10;
void (^stackBlock)() = ^{
NSLog(@"%d", value);
};
// 这里的stackBlock是NSStackBlock类型
}
- NSMallocBlock:堆上的Block,需要手动或自动地进行内存管理(通过
copy
和release
等操作)。通常在将栈上的Block拷贝到堆上时(例如将Block作为参数传递给需要持有它的函数,或者将Block赋值给一个强引用的属性等情况),会变成NSMallocBlock类型。例如:
@property (nonatomic, strong) void (^heapBlock)();
- (void)assignBlock {
int value = 10;
void (^stackBlock)() = ^{
NSLog(@"%d", value);
};
self.heapBlock = stackBlock; // stackBlock被拷贝到堆上,变为NSMallocBlock
}
- NSGlobalBlock:全局的Block,存储在程序的全局数据区,生命周期伴随整个程序。当Block没有捕获任何外部变量时,会是这种类型。例如:
void (^globalBlock)() = ^{
NSLog(@"Global Block");
};
性能优化
避免不必要的捕获
- 具体场景:在一个频繁调用的方法中,Block捕获了大量不必要的外部变量。
- 代码示例:
@interface ViewController ()
@property (nonatomic, strong) NSArray *largeDataArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.largeDataArray = @[@"a", @"b", @"c", @"d", @"e", ..., @"z"]; // 假设有大量数据
for (int i = 0; i < 1000; i++) {
[self performTaskWithBlock:^{
NSLog(@"%@", self.largeDataArray); // Block捕获了largeDataArray
}];
}
}
- (void)performTaskWithBlock:(void (^)())block {
block();
}
@end
- 优化方法:只捕获真正需要的变量,避免捕获大对象。可以在Block内部重新获取需要的数据,而不是直接捕获。例如:
@interface ViewController ()
@property (nonatomic, strong) NSArray *largeDataArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.largeDataArray = @[@"a", @"b", @"c", @"d", @"e", ..., @"z"];
for (int i = 0; i < 1000; i++) {
NSArray *dataToUse = self.largeDataArray; // 先获取需要的数据
[self performTaskWithBlock:^{
NSLog(@"%@", dataToUse); // 捕获小的局部变量
}];
}
}
- (void)performTaskWithBlock:(void (^)())block {
block();
}
@end
合理管理Block的生命周期
- 具体场景:在一个视图控制器中,有一个Block作为属性,且该Block捕获了视图控制器本身(
self
),可能会导致循环引用,从而引起内存泄漏。 - 代码示例:
@interface ViewController ()
@property (nonatomic, strong) void (^block)(void);
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.block = ^{
NSLog(@"%@", self); // Block捕获了self
};
}
@end
- 优化方法:使用
__weak
或者__unsafe_unretained
修饰符来打破循环引用。例如:
@interface ViewController ()
@property (nonatomic, strong) void (^block)(void);
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.block = ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
NSLog(@"%@", strongSelf);
}
};
}
@end
减少Block的创建次数
- 具体场景:在一个循环中频繁创建相同逻辑的Block。
- 代码示例:
for (int i = 0; i < 1000; i++) {
void (^block)() = ^{
NSLog(@"Block execution");
};
block();
}
- 优化方法:将Block的创建移到循环外部,避免每次循环都创建新的Block对象。例如:
void (^block)() = ^{
NSLog(@"Block execution");
};
for (int i = 0; i < 1000; i++) {
block();
}