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

本文主講Block相關(guān)面試問題,包括Block介紹、截獲變量、__block修飾符、Block的內(nèi)存管理、Block的循環(huán)引用。

一、Block介紹

Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。

二、截獲變量。包括局部變量(基本數(shù)據(jù)類型和對象類型)、靜態(tài)局部變量、全部變量、靜態(tài)全部變量。

對于基本數(shù)據(jù)類型的局部變量截獲其值。
對于對象類型的局部變量連同所有權(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修飾符

一般情況下,對被截獲變量進(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為賦值,存在問題,會編譯報錯,需要用__block修飾被截獲變量array2。

對變量進(jìn)行賦值時,對于局部變量(基本數(shù)據(jù)類型和對象類型),需要__block修飾符。對于靜態(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又會持有self,所以會導(dǎo)致循環(huán)引用,這個例子非常好,因為xcode都能檢測出來,報出警告:[capturing self strongly in this block is likely to lead to a retain cycle],當(dāng)然大部分循環(huán)引用的情況xcode是不會報警告的。
(2)、這時Block對象雖然捕獲了weakSelf,延長了weakSelf這個局部變量的生命周期,但weakSelf是附有__weak修飾符的變量,它并不會持有對象,一旦它指向的對象被廢棄了,它將自動被賦值為nil。在多線程情況下,可能weakSelf指向的對象會在Block執(zhí)行前被廢棄,這樣可能頂多就是返回nil,但在有些情況下(譬如在Block中有移除KVO的觀察者的邏輯,在執(zhí)行到該邏輯前self就釋放了)就會導(dǎo)致crash。這時可以在Block內(nèi)部(第一句)再持有一次weakSelf指向的對象,保證在執(zhí)行Block期間該對象不會被廢棄,這就是所謂的 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又會持有ClassA,所以會導(dǎo)致循環(huán)引用。

最后總結(jié)點東西吧,面試官經(jīng)常問的問題有:

什么是block?
為什么Block會產(chǎn)生循環(huán)引用?
咋樣理解Block截獲變量的特性?
你都遇見過哪些循環(huán)引用?你又是咋樣解決的?

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

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

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