Block相關(guān)面試問題

本文主講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。

屏幕快照 2019-11-10 下午4.36.07.png

2、Block的copy操作

屏幕快照 2019-11-10 下午4.54.22.png

五、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)引用?你又是咋樣解決的?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容