iOS Block簡介

Block的本質

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

#import "CLBlock.h"

@implementation CLBlock
- (void)blockTest {
    int multiplier = 6;
    int(^BlockTest)(int) = ^int(int num){
        return num * multiplier;
    };
    BlockTest(2);
}
@end

clang(LLVM編譯器)具有轉換為我們可讀源代碼的功能。通過“-rewrite-objc”選項就能將含有Block語法的源代碼變換為C++的源代碼。說是C++,其實也僅是使用了struct結構,其本質是C語言源代碼。
使用【clang -rewrite-objc CLBlock.m】進行源碼解析,查看編譯后的文件內容。

// @implementation CLBlock
//Block結構體
struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
  int multiplier;
  //構造函數(shù)
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) {
    impl.isa = &_NSConcreteStackBlock;//isa指針,Block是對象的標志
    impl.Flags = flags;
    impl.FuncPtr = fp;//函數(shù)指針
    Desc = desc;
  }
};
/**函數(shù)
第一個參數(shù):Block結構體
第二個參數(shù):傳入?yún)?shù)
*/
static int __CLBlock__blockTest_block_func_0(struct __CLBlock__blockTest_block_impl_0 *__cself, int num) {
  int multiplier = __cself->multiplier; // bound by copy

        return num * multiplier;
    }

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0)};

static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {
    int multiplier = 6;
    
/*
    int(^BlockTest)(int) = ^int(int num){
        return num * multiplier;
    };
*/
    int(*BlockTest)(int) = ((int (*)(int))&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, multiplier));
    /*
BlockTest(2);
*/
    ((int (*)(__block_impl *, int))((__block_impl *)BlockTest)->FuncPtr)((__block_impl *)BlockTest, 2);
}
// @end

struct __block_impl {
  void *isa;//isa指針,Block是對象的標志
  int Flags; //標志變量,在實現(xiàn)block的內部操作時會用到
  int Reserved;//保留變量
  void *FuncPtr;//函數(shù)指針
};

通過編譯后的源碼得知,block編譯后為一個含有isa指針的結構體,所以可以將block當做對象;而block的上下文內容被編譯后一個函數(shù)。而調用時便是將編譯后的上下文函數(shù)作為參數(shù)使用。

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;//保留字段
  size_t Block_size;//block大小(sizeof(struct __main_block_impl_0))
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0)};

在定義__CLBlock__blockTest_block_desc_0結構體時,同時創(chuàng)建了__CLBlock__blockTest_block_desc_0_DATA,并給它賦值,以供在blockTest函數(shù)中對__CLBlock__blockTest_block_impl_0進行初始化。

截獲變量

為了保證 block 內部能夠正常訪問外部的變量,block 有一個變量捕獲機制。

  • 對于基本數(shù)據(jù)類型的局部變量截獲其值。
  • 對于對象類型的局部變量連同所有權修飾符一起截獲。
  • 指針形式截獲局部靜態(tài)變量。
  • 不截獲全局變量、靜態(tài)全局變量
#import "CLBlock.h"
#import "Test.h"

@implementation CLBlock
int global_var = 4;//全局變量
static int static_global_var = 5;//靜態(tài)全局變量

- (void)blockTest {
    
    //基本數(shù)據(jù)類型的局部變量
    int var = 6;
    
    //對象類型的局部變量
    __unsafe_unretained id unsafe_objc = nil;
    __strong id strong_obj = nil;
    Test *test = nil;
    
    //靜態(tài)局部變量
    static int static_var = 7;
    
    void(^BlockTest)(void) = ^{
        NSLog(@"global_var==%d",global_var);
        NSLog(@"static_global_var==%d",static_global_var);
        NSLog(@"var==%d",var);
        NSLog(@"unsafe_objc==%@",unsafe_objc);
        NSLog(@"strong_obj==%@",strong_obj);
        NSLog(@"test==%@",test);
        NSLog(@"static_var==%d",static_var);
    };
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = [[NSObject alloc] init];
    strong_obj = [[NSObject alloc] init];
    test = [[Test alloc] init];
    static_var = 71;
    
    BlockTest();
}
@end

運行結果

global_var==41
static_global_var==51
var==6
unsafe_objc==(null)
strong_obj==(null)
test==(null)
static_var==71

使用【clang -rewrite-objc -fobjc-arc CLBlock.m】命令進行源碼解析

int global_var = 4;
static int static_global_var = 5;


struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
    //截獲基本數(shù)據(jù)類型的局部變量的值
  int var;
    //對象類型的局部變量,其值連同所有權修飾符一起截獲
  __unsafe_unretained id unsafe_objc;
  __strong id strong_obj;
  Test *__strong test;
    //以指針形式截獲靜態(tài)局部變量
  int *static_var;
    //對全局變量、靜態(tài)全局變量不截獲
    
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_objc, __strong id _strong_obj, Test *__strong _test, int *_static_var, int flags=0) : var(_var), unsafe_objc(_unsafe_objc), strong_obj(_strong_obj), test(_test), static_var(_static_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __CLBlock__blockTest_block_func_0(struct __CLBlock__blockTest_block_impl_0 *__cself) {
  int var = __cself->var; // bound by copy
  id unsafe_objc = __cself->unsafe_objc; // bound by copy
  id strong_obj = __cself->strong_obj; // bound by copy
  Test *test = __cself->test; // bound by copy
  int *static_var = __cself->static_var; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_0,global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_1,static_global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_2,var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_3,unsafe_objc);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_4,strong_obj);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_5,test);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_by_brpygpw16r5cz_vx4d7_prcw0000gn_T_CLBlock_a61b60_mi_6,(*static_var));
    }
static void __CLBlock__blockTest_block_copy_0(struct __CLBlock__blockTest_block_impl_0*dst, struct __CLBlock__blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->unsafe_objc, (void*)src->unsafe_objc, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->strong_obj, (void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->test, (void*)src->test, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __CLBlock__blockTest_block_dispose_0(struct __CLBlock__blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->unsafe_objc, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->test, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __CLBlock__blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __CLBlock__blockTest_block_impl_0*, struct __CLBlock__blockTest_block_impl_0*);
  void (*dispose)(struct __CLBlock__blockTest_block_impl_0*);
} __CLBlock__blockTest_block_desc_0_DATA = { 0, sizeof(struct __CLBlock__blockTest_block_impl_0), __CLBlock__blockTest_block_copy_0, __CLBlock__blockTest_block_dispose_0};

static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {


    int var = 6;


    __attribute__((objc_ownership(none))) id unsafe_objc = __null;
    __attribute__((objc_ownership(strong))) id strong_obj = __null;
    Test *test = __null;


    static int static_var = 7;

    void(*BlockTest)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, var, unsafe_objc, strong_obj, test, &static_var, 570425344));
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    strong_obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    test = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init"));
    static_var = 71;

    ((void (*)(__block_impl *))((__block_impl *)BlockTest)->FuncPtr)((__block_impl *)BlockTest);
}

__block修飾符

對被截獲的局部變量進行賦值操作需要__block修飾符;
對于靜態(tài)局部變量、全局變量、靜態(tài)全局變量不需要__block修飾符;

賦值≠使用
    NSMutableArray *mArray = [[NSMutableArray alloc] init];
    void(^Block)(void) = ^{
        [mArray addObject:@123];
    };
    Block();

此種情況不需要__block修飾符。

__block原理

__block修飾的變量變成了對象。

示例:

#import "CLBlock.h"
#import "Test.h"

@implementation CLBlock
int global_var = 4;//全局變量
static int static_global_var = 5;//靜態(tài)全局變量

- (void)blockTest {
    
    //基本數(shù)據(jù)類型的局部變量
    __block int var = 6;
    
    //對象類型的局部變量
    __block __unsafe_unretained id unsafe_objc = nil;
    __block __strong id strong_obj = nil;
    __block Test *test = nil;
    
    //靜態(tài)局部變量
    static int static_var = 7;
    
    void(^BlockTest)(void) = ^{
        NSLog(@"global_var==%d",global_var);
        NSLog(@"static_global_var==%d",static_global_var);
        NSLog(@"var==%d",var);
        NSLog(@"unsafe_objc==%@",unsafe_objc);
        NSLog(@"strong_obj==%@",strong_obj);
        NSLog(@"test==%@",test);
        NSLog(@"static_var==%d",static_var);
    };
    global_var = 41;
    static_global_var = 51;
    var = 61;
    unsafe_objc = [[NSObject alloc] init];
    strong_obj = [[NSObject alloc] init];
    test = [[Test alloc] init];
    static_var = 71;
    
    BlockTest();
    
    
    NSMutableArray *mArray = [[NSMutableArray alloc] init];
    void(^Block)(void) = ^{
        [mArray addObject:@123];
    };
    Block();
}
@end

運行結果:

global_var==41
static_global_var==51
var==61
unsafe_objc==<NSObject: 0x600003ddc940>
strong_obj==<NSObject: 0x600003ddc940>
test==<Test: 0x600003ddc970>
static_var==71

查看源碼編譯:

int global_var = 4;
static int static_global_var = 5;

struct __Block_byref_var_0 {
  void *__isa;
__Block_byref_var_0 *__forwarding;
 int __flags;
 int __size;
 int var;
};
struct __Block_byref_unsafe_objc_1 {
  void *__isa;
__Block_byref_unsafe_objc_1 *__forwarding;
 int __flags;
 int __size;
 __unsafe_unretained id unsafe_objc;
};
struct __Block_byref_strong_obj_2 {
  void *__isa;
__Block_byref_strong_obj_2 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 __strong id strong_obj;
};
struct __Block_byref_test_3 {
  void *__isa;
__Block_byref_test_3 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Test *__strong test;
};

struct __CLBlock__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __CLBlock__blockTest_block_desc_0* Desc;
  int *static_var;
  __Block_byref_var_0 *var; // by ref
  __Block_byref_unsafe_objc_1 *unsafe_objc; // by ref
  __Block_byref_strong_obj_2 *strong_obj; // by ref
  __Block_byref_test_3 *test; // by ref
  __CLBlock__blockTest_block_impl_0(void *fp, struct __CLBlock__blockTest_block_desc_0 *desc, int *_static_var, __Block_byref_var_0 *_var, __Block_byref_unsafe_objc_1 *_unsafe_objc, __Block_byref_strong_obj_2 *_strong_obj, __Block_byref_test_3 *_test, int flags=0) : static_var(_static_var), var(_var->__forwarding), unsafe_objc(_unsafe_objc->__forwarding), strong_obj(_strong_obj->__forwarding), test(_test->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
......
此處省略部分代碼
......
static void _I_CLBlock_blockTest(CLBlock * self, SEL _cmd) {


    __attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 6};


    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(none))) __Block_byref_unsafe_objc_1 unsafe_objc = {(void*)0,(__Block_byref_unsafe_objc_1 *)&unsafe_objc, 0, sizeof(__Block_byref_unsafe_objc_1), __null};
    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(strong))) __Block_byref_strong_obj_2 strong_obj = {(void*)0,(__Block_byref_strong_obj_2 *)&strong_obj, 33554432, sizeof(__Block_byref_strong_obj_2), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, __null};
    __attribute__((__blocks__(byref))) __Block_byref_test_3 test = {(void*)0,(__Block_byref_test_3 *)&test, 33554432, sizeof(__Block_byref_test_3), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, __null};


    static int static_var = 7;

    void(*BlockTest)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_0((void *)__CLBlock__blockTest_block_func_0, &__CLBlock__blockTest_block_desc_0_DATA, &static_var, (__Block_byref_var_0 *)&var, (__Block_byref_unsafe_objc_1 *)&unsafe_objc, (__Block_byref_strong_obj_2 *)&strong_obj, (__Block_byref_test_3 *)&test, 570425344));
    global_var = 41;
    static_global_var = 51;
    (var.__forwarding->var) = 61;
    (unsafe_objc.__forwarding->unsafe_objc) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    (strong_obj.__forwarding->strong_obj) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    (test.__forwarding->test) = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init"));
    static_var = 71;

    ((void (*)(__block_impl *))((__block_impl *)BlockTest)->FuncPtr)((__block_impl *)BlockTest);


    NSMutableArray *mArray = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
    void(*Block)(void) = ((void (*)())&__CLBlock__blockTest_block_impl_1((void *)__CLBlock__blockTest_block_func_1, &__CLBlock__blockTest_block_desc_1_DATA, mArray, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}

以基本數(shù)據(jù)類型為例,通過與添加__block修飾符之前源碼對比發(fā)現(xiàn):
int類型變?yōu)?code>__Block_byref_var_0*對象類型;
賦值時由var = 61;變?yōu)?code>(var.__forwarding->var) = 61;

__forwarding.png

Block的內存管理

根據(jù)Block在內存中的位置分為三種類型:

  • 全局類型:_NSConcreteGlobalBlock,位于全局區(qū)的block,它是設置在程序的數(shù)據(jù)區(qū)域(.data區(qū))中。
  • 棧類型:_NSConcreteStackBlock,位于棧區(qū),超出變量作用域,棧上的Block以及 __block變量都被銷毀。
  • 堆類型:_NSConcreteMallocBlock位于堆區(qū),在變量作用域結束時不受影響。
_NSConcreteGlobalBlock

生成全局類型Block有兩種情況

  • 定義全局變量的地方有Block語法時
#import "CLBlock.h"


@implementation CLBlock

void(^BlockTest)(void) = ^{ };

- (void)blockTest {
   
}
@end

編譯后

struct __BlockTest_block_impl_0 {
  struct __block_impl impl;
  struct __BlockTest_block_desc_0* Desc;
  __BlockTest_block_impl_0(void *fp, struct __BlockTest_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • Block語法不使用截獲的自動變量時候
    int (^BlockTest)(int num) = ^(int num){
        return num;
    };
    BlockTest(2);

雖然,這個Block在循環(huán)內,但是Block的地址總是不變的。說明這個Block在全局段。注:針對沒有捕獲自動變量的Block來說,雖然用clang的rewrite-objc轉化后的代碼中仍顯示_NSConcretStackBlock,但是實際上不是這樣的。????

_NSConcreteStackBlock

設置在棧上的Block,如果其作用域結束,該Block就被銷毀。同樣的,由于__block變量也配置在棧上,如果其作用域結束,則該__block變量也會被銷毀。

_NSConcreteMallocBlock

堆類型Block無法直接創(chuàng)建,需要由_NSConcreteStackBlock類型的block拷貝而來(也就是說block需要執(zhí)行copy之后才能存放到堆中)。

  • 棧Block被拷貝生成到堆情況

1.調用Block的copy實例方法時
2.Block作為函數(shù)返回值返回時
3.將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時
4.將方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時

Block的copy操作
copy.png
Block由棧 copy至堆的內存使用變化
  • 當棧上的Block被復制到堆上時,__block變量也會隨之復制,并且Block持有該變量


    單個Block使用__block變量.png
  • 多個Block中使用__block變量時,任何一個Block被復制到堆時,__block變量也會一并復制到堆并被持有,其余Block被復制時,僅需要__block 變量的引用計數(shù)


    多個Block中使用__block變量.png

和使用引用計數(shù)一樣,在堆上的Block被廢棄,它所引用的__block變量將被釋放


廢棄Block.png

理解Block作用域之后,我們發(fā)現(xiàn)這和OC引用計數(shù)方式管理方式一樣,使用__block修飾符來持有對象,當Block被廢棄之后,__block修飾變量也隨之釋放

當Block被復制到堆之后,會將自身的__forwarding指針更新,依然指向“最新”的自己,這樣就保證了在棧上或者堆上都能正確訪問對應變量


復制之后.png

__forwarding無論在任何內存位置,都可以順利的訪問同一個__block變量。

Block的循環(huán)引用

循環(huán)引用

當前對象持有block,在block中使用self又造成block持有self,引起循環(huán)引用。

- (void)blockTest {
    
    _block = ^{
        NSLog(@"%@",self.test);
    };
       
}

使用__weak關鍵字解除循環(huán)引用:

- (void)blockTest {
    
    __weak typeof(self)weakSelf = self;
    _block = ^{
        NSLog(@"%@",weakSelf.test);
    };
  
}
__block引起的循環(huán)引用

注意:
MRC 下,__block修改對象不會引起其引用計數(shù),避免了循環(huán)引用。
ARC下,__block修改對象會引起強引用,無法避免了循環(huán)引用,需要手動解環(huán)。

- (void)blockTest {
    
    __block CLBlock *blockSelf = self;
    _block = ^{
        NSLog(@"%@",blockSelf.test);
    };
    _block();
}

對象持有Block,Block持有__block變量,__block變量持有對象,引起大環(huán)引用。
解決方式:

- (void)blockTest {
    
    __block CLBlock *blockSelf = self;
    _block = ^{
        NSLog(@"%@",blockSelf.test);
        blockSelf = nil;
    };
    _block();
}

注意:如果Block一直不調用,無法解環(huán)。

Block對對象的長引用

Block 中如果持有了self就會對其強引用,項目中有許多地方在Block中持有self并不會引起循環(huán)引用(如在封裝的網(wǎng)絡請求中),但是可能持有較長的時間,建議此時使用__weak進行弱引用處理。

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{}
                                successBlock:^(id  _Nullable responseObject) {
        weakSelf.responseObject = responseObject;
    } errorBlock:^(NSError *error) {

    } failureBlock:^(NSError *error) {

    }];

__strong使用

如果網(wǎng)絡異步返回Block,Block執(zhí)行的過程中頁面返回,由于__weak對對象進行弱引用,則此時對象會被釋放,異步運行中的對象變成nil,引起異常。

示例:

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{} successBlock:^(id  _Nullable responseObject) {
        NSLog(@"weakSelf對象地址:%@",weakSelf);
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%d",i);
        }
        NSLog(@"耗時的任務 結束 weakSelf對象地址:%@",weakSelf);
    } errorBlock:^(NSError * _Nonnull error) {
        
    } failureBlock:^(NSError * _Nonnull error) {
        
    }];

打印結果

weakSelf對象地址:<AViewController: 0x7fc2ffd06c10>
...
省略
...
耗時的任務 結束 weakSelf對象地址:(null)

此時需要在Block中使用__strong修飾符,在Block開始執(zhí)行時,檢驗弱引用的對象是否還存在,如果存在,使用__strong進行強引用,此時引用計數(shù)加1,這樣在Block執(zhí)行的過程中,這個對象就不會被置為nil,而在Block執(zhí)行完畢后,對象的引用計數(shù)就會減1,這樣就不會導致對象無法釋放。

    __weak typeof(self)weakSelf = self;
    [[NetworkRequest sharedInstance] request:@{} successBlock:^(id  _Nullable responseObject) {
        if (!weakSelf) return;
        __strong typeof(weakSelf)strongSelf = weakSelf;
        NSLog(@"strongSelf對象地址:%@",strongSelf);
        for (int i = 0; i < 10000; i++) {
            // 模擬一個耗時的任務
            NSLog(@"%d",i);
        }
        NSLog(@"耗時的任務 結束 strongSelf對象地址:%@",strongSelf);
    } errorBlock:^(NSError * _Nonnull error) {
        
    } failureBlock:^(NSError * _Nonnull error) {
        
    }];

運行結果:

strongSelf對象地址:<AViewController: 0x7f9f5371ee60>
...
省略
...
耗時的任務 結束 strongSelf對象地址:<AViewController: 0x7f9f5371ee60>

區(qū)別:
直接使用self,會在編譯時強引用self,引起循環(huán)引用;
使用__weak后再在Block中使用__strong,則只會在運行到Block中時才會強引用,引用計數(shù)進行加1操作,Block執(zhí)行完畢后引用計數(shù)減1。


  • 參考及內容引用資料

iOS中Block的用法,舉例,解析與底層原理(這可能是最詳細的Block解析)
(四)Block之 __block修飾符及其存儲域
iOS中Block實現(xiàn)原理的全面分析
__weak和__strong在Block中的使用
Block的本質與使用

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

友情鏈接更多精彩內容