本文主講Block相關(guān)面試問題,包括Block介紹、截獲變量、__block修飾符、Block的內(nèi)存管理、Block的循環(huán)引用。
一、Block介紹
Block是將函數(shù)及其執(zhí)行上下文封裝起來的對(duì)象。
二、截獲變量。包括局部變量(基本數(shù)據(jù)類型和對(duì)象類型)、靜態(tài)局部變量、全部變量、靜態(tài)全部變量。
對(duì)于基本數(shù)據(jù)類型的局部變量截獲其值。
對(duì)于對(duì)象類型的局部變量連同所有權(quán)修飾符一起截獲。
以指針形式截獲靜態(tài)局部變量。
不截獲全局變量、靜態(tài)全局變量。
int multiplier = 6;
int(^Block)(int) = ^int(int num){
return num*multiplier;
};
multiplier = 4;
NSLog(@"result is %d",Block(2)); //result is 12
三、__block修飾符
一般情況下,對(duì)被截獲變量進(jìn)行賦值操作需添加__block修飾符。
注意:賦值不等于使用。
NSMutableArray *array =[NSMutableArray array];
void(^Block)(void) = ^{
[array addObject:@"123"];
};
Block(); //該block為使用,不存在問題。
NSMutableArray *array2 =nil;
void(^Block2)(void) = ^{
array2 = [NSMutableArray array];
};
Block2(); //該block為賦值,存在問題,會(huì)編譯報(bào)錯(cuò),需要用__block修飾被截獲變量array2。
對(duì)變量進(jìn)行賦值時(shí),對(duì)于局部變量(基本數(shù)據(jù)類型和對(duì)象類型),需要__block修飾符。對(duì)于靜態(tài)局部變量、全局變量、靜態(tài)全局變量,則不需要__block修飾符。
四、Block的內(nèi)存管理
1、Block類型:NSGlobalBlock、NSStackBlock、NSMallocBlock。

2、Block的copy操作

五、Block的循環(huán)引用
1、相互循環(huán)引用
typedef void(^Block)(NSString*);
@interface ClassA()
{
int age;
}
@property(nonatomic,strong)Block block;
@property(nonatomic,strong)NSString *temp;
@end
@implementation ClassA
- (void)viewDidLoad {
[super viewDidLoad];
self.block = ^(NSString * content) {
self.temp = content;
self->age = 15;
};
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.block(@"趙四");
}
(1)、如上代碼,self持有block,而堆上的block又會(huì)持有self,所以會(huì)導(dǎo)致循環(huán)引用,這個(gè)例子非常好,因?yàn)閤code都能檢測(cè)出來,報(bào)出警告:[capturing self strongly in this block is likely to lead to a retain cycle],當(dāng)然大部分循環(huán)引用的情況xcode是不會(huì)報(bào)警告的。
(2)、這時(shí)Block對(duì)象雖然捕獲了weakSelf,延長(zhǎng)了weakSelf這個(gè)局部變量的生命周期,但weakSelf是附有__weak修飾符的變量,它并不會(huì)持有對(duì)象,一旦它指向的對(duì)象被廢棄了,它將自動(dòng)被賦值為nil。在多線程情況下,可能weakSelf指向的對(duì)象會(huì)在Block執(zhí)行前被廢棄,這樣可能頂多就是返回nil,但在有些情況下(譬如在Block中有移除KVO的觀察者的邏輯,在執(zhí)行到該邏輯前self就釋放了)就會(huì)導(dǎo)致crash。這時(shí)可以在Block內(nèi)部(第一句)再持有一次weakSelf指向的對(duì)象,保證在執(zhí)行Block期間該對(duì)象不會(huì)被廢棄,這就是所謂的 weak-strong。
解決這種循環(huán)引用的常用方式如下
typedef void(^Block)(NSString*);
@interface ClassA()
{
int age;
}
@property(nonatomic,strong)Block block;
@property(nonatomic,strong)NSString *temp;
@end
@implementation ClassA
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.block = ^(NSString * content) {
__strong typeof(weakSelf)strongSelf = weakSelf;
strongSelf.temp = content;
strongSelf->age = 15;
};
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.block(@"趙四");
}
2、多循環(huán)引用
typedef void(^Block)(NSString*);
@interface ClassB : UIViewController
@property(nonatomic,strong)Block block;
#import "ClassA.h"
#import "ClassB.h"
@interface ClassA()
@property(nonatomic,strong)ClassB *classB;
@property(nonatomic,strong)NSString *temp;
@end
@implementation ClassA
- (void)viewDidLoad {
[super viewDidLoad];
self.classB = [[ClassB alloc]init];
self.classB.block = ^(NSString *content) {
self.temp = content;
};
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.classB.block(@"趙四");
}
@end
如上代碼,ClassA持有ClassB,ClassB持有block,而堆上的block又會(huì)持有ClassA,所以會(huì)導(dǎo)致循環(huán)引用。
最后總結(jié)點(diǎn)東西吧,面試官經(jīng)常問的問題有:
什么是block?
為什么Block會(huì)產(chǎn)生循環(huán)引用?
咋樣理解Block截獲變量的特性?
你都遇見過哪些循環(huán)引用?你又是咋樣解決的?