iOS中的Block

什么是Block(快速實(shí)現(xiàn)直接輸入inlink)

Block是一種特殊的數(shù)據(jù)類型

Block的作用

  • 用于保存一段代碼,可以在恰當(dāng)?shù)臅r(shí)間取出來(lái)調(diào)用
  • 功能類似于函數(shù)和方法

Block的格式

返回值(^block變量名)(形參列表) =  ^( 形參列表){

}捂刺;
  • 無(wú)參數(shù)無(wú)返回值
    void (^sunBlock)();
    sunBlock = ^{
      NSLog(@"sunBlock");
    };
    sunBlock();
    
  • 有參數(shù)無(wú)返回值
    void(^sunBlock)(int,int);
    sunBlock = ^(int value1,int value2){
       NSLog(@"%d",value1 + value2);
    };
    sunBlock(10,20);
    
  • 有參數(shù)有返回值
    int (^sunBlock)(int,int);
    sunBlock = ^(int value1,int value2){
        return value1 + value2;
    };
    
    NSLog(@"%d",sunBlock(10,20));
    
    

typedef 和Block

利用typedef給block起別名,和指向函數(shù)的指針一樣婴削,block變量的名稱就是別名


typedef int (^calculateBlock)(int,int);
int main(int argc, const char * argv[]) {
    
    calculateBlock sumBlock  = ^(int value1,int value2){
        return value1 + value2;
    };
    NSLog(@"%d",sumBlock(20,10));
        
    calculateBlock minusBlock  = ^(int value1,int value2){
        return value1 - value2;
    };
    NSLog(@"%d",minusBlock(20,10));
}

Block的底層實(shí)現(xiàn)

  • 原文件:
int main(int argc, const char * argv[]) {
    ^{ };
    return 0;
}
  • 通過(guò)clang命令將OC轉(zhuǎn)為C++代碼來(lái)查看一下Block底層實(shí)現(xiàn),clang命令使用方式為終端使用cd定位到main.m文件所在文件夾,然后利用clang -rewrite-objc main.m將OC轉(zhuǎn)為C++,成功后在main.m同目錄下會(huì)生成一個(gè)main.cpp文件
    struct __block_impl {
       void *isa; //isa晦炊,指向所屬類的指針璧瞬,也就是block的類型
       int Flags; //flags,標(biāo)志變量麻裁,在實(shí)現(xiàn)block的內(nèi)部操作時(shí)會(huì)用到
       int Reserved; //Reserved箍镜,保留變量
       void *FuncPtr; //block執(zhí)行時(shí)調(diào)用的函數(shù)指針
   };

   struct __main_block_impl_0 {
     struct __block_impl impl;
     struct __main_block_desc_0* Desc;
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
       impl.isa = &_NSConcreteStackBlock;  //__main_block_impl_0的isa指針指向了_NSConcreteStackBlock
       impl.Flags = flags;
       impl.FuncPtr = fp; //從main函數(shù)中看, __main_block_impl_0的FuncPtr指向了函數(shù)__main_block_func_0
       Desc = desc; //__main_block_impl_0的Desc也指向了定義__main_block_desc_0時(shí)就創(chuàng)建的__main_block_desc_0_DATA煎源,其中紀(jì)錄了block結(jié)構(gòu)體大小等信息色迂。
         }
   };

       static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

   }

    static struct __main_block_desc_0 {
         size_t reserved; //保留字段
     size_t Block_size; //block大小(sizeof(struct __main_block_impl_0))
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
//以上代碼在定義__main_block_desc_0結(jié)構(gòu)體時(shí),同時(shí)創(chuàng)建了__main_block_desc_0_DATA手销,并給它賦值歇僧,以供在main函數(shù)中對(duì)__main_block_impl_0進(jìn)行初始化。

   int main(int argc, const char * argv[]) {

       ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

       return 0;
   }

1锋拖、__block_impl結(jié)構(gòu)體诈悍,它包含了isa指針(包含isa指針的皆為對(duì)象),也就是說(shuō)block也是一個(gè)對(duì)象
2兽埃、__main_block_impl_0結(jié)構(gòu)體侥钳,可以看出是根據(jù)所在函數(shù)(main函數(shù))以及出現(xiàn)序列(第0個(gè))進(jìn)行命名的,如果是全局的blcok柄错,就根據(jù)變量名和出現(xiàn)序列進(jìn)行命名舷夺。
3苦酱、__main_block_impl_0中包含了倆個(gè)成員變量和一個(gè)構(gòu)造函數(shù),成員變量分別是__block_impl結(jié)構(gòu)體和描述信息Desc给猾,之后在構(gòu)造函數(shù)中初始化block的類型信息和函數(shù)指針等信息疫萤。
4、__main_block_func_0函數(shù)敢伸,其實(shí)對(duì)應(yīng)的block的函數(shù)體扯饶,該函數(shù)接受了一個(gè)__cself參數(shù),其實(shí)就是對(duì)應(yīng)的block本身
5详拙、__main_block_desc_0結(jié)構(gòu)體帝际,其中比較有價(jià)值的信息是block的大小
6蔓同、main函數(shù)對(duì)block的創(chuàng)建饶辙,可以看出執(zhí)行block就是調(diào)用一個(gè)以block自身為參數(shù)的函數(shù),這個(gè)函數(shù)對(duì)應(yīng)著block的執(zhí)行體斑粱。

Block的分類

  • NSConcreteGlobalBlock 全局的靜態(tài) block弃揽,不會(huì)訪問(wèn)任何外部變量。
  • NSConcreteStackBlock 保存在棧中的 block则北,當(dāng)函數(shù)返回時(shí)會(huì)被銷毀矿微。
  • NSConcreteMallocBlock 保存在堆中的 block,當(dāng)引用計(jì)數(shù)為 0 時(shí)會(huì)被銷毀尚揣。
NSConcreteGlobalBlock 類型的 block 的實(shí)現(xiàn)
void (^testGlobalBlock)() = ^{
    NSLog(@"hello block");
};
int main(int argc, const char * argv[]) {
    testGlobalBlock();
    return 0;
}

testGlobalBlock的isa指向了_NSConcreteGlobalBlock涌矢,即在全局區(qū)域創(chuàng)建,block變量存儲(chǔ)在全局?jǐn)?shù)據(jù)存儲(chǔ)區(qū)

NSConcreteStackBlock 類型的 block 的實(shí)現(xiàn)
int main(int argc, const char * argv[]) {
    void (^testStackBlock)() = ^{
        NSLog(@"hello block");
    };
    testStackBlock();
    return 0;
}

testStackBlock的isa指向了_NSConcreteStackBlock快骗,即在棧區(qū)創(chuàng)建娜庇。

NSConcreteMallocBlock 類型的 block 的實(shí)現(xiàn)
int main(int argc, const char * argv[]) {
   void (^testStackBlock)() = [^{
        NSLog(@"hello block");
    } copy];
    testStackBlock();
    return 0;
}

NSConcreteMallocBlock 類型的 block 通常不會(huì)在源碼中直接出現(xiàn),其需要由_NSConcreteStackBlock類型的block拷貝而來(lái)(也就是說(shuō)block需要執(zhí)行copy之后才能存放到堆中)方篮。

其內(nèi)部通過(guò)函數(shù)memmove將棧中的block的內(nèi)容拷貝到了堆中名秀,并使isa指向了_NSConcreteMallocBlock。

block主要的一些學(xué)問(wèn)就出在棧中block向堆中block的轉(zhuǎn)移過(guò)程中了藕溅。

Block的應(yīng)用

Block中訪問(wèn)局部變量
  • 在Block中訪問(wèn)局部變量
int main(int argc, const char * argv[]) {
    int testNum = 10;
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:10
  • 在聲明Block之后匕得,調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之前的舊值
int main(int argc, const char * argv[]) {
    int testNum = 10;
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNum = 20;
    testNumBlock();
    return 0;
}
打印結(jié)果:10
  • 在Block中不可以直接修改局部變量
int main(int argc, const char * argv[]) {
    int testNum = 10;
    void(^testNumBlock)() = ^{
        testNum = 20; //報(bào)錯(cuò)
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
Block內(nèi)訪問(wèn)__block修飾的局部變量
  • 在局部變量前使用下劃線下劃線block修飾,在聲明Block之后巾表、調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之后的新值
__block int testNum = 10;
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNum = 20;
    testNumBlock();
打印結(jié)果:20
  • 在局部變量前使用下劃線下劃線block修飾,在Block中可以直接修改局部變量
int main(int argc, const char * argv[]) {
    __block int testNum = 10;
    void(^testNumBlock)() = ^{
        testNum = 20;
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:20
Block內(nèi)訪問(wèn)全局變量
  • 在Block中可以訪問(wèn)全局變量
int testNum = 10;
int main(int argc, const char * argv[]) {
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:10
  • 在聲明Block之后汁掠、調(diào)用Block之前對(duì)全局變量進(jìn)行修改,在調(diào)用Block時(shí)全局變量值是修改之后的新值
int testNum = 10;
int main(int argc, const char * argv[]) {
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNum = 20;
    testNumBlock();
    return 0;
}
打印結(jié)果:20
  • 在Block中可以直接修改全局變量
int testNum = 10;
int main(int argc, const char * argv[]) {
    void(^testNumBlock)() = ^{
        testNum = 20;
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:20
Block內(nèi)訪問(wèn)靜態(tài)變量
  • 在Block中可以訪問(wèn)靜態(tài)變量
int main(int argc, const char * argv[]) {
    static int testNum = 10;
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:10
  • 在聲明Block之后、調(diào)用Block之前對(duì)靜態(tài)變量進(jìn)行修改,在調(diào)用Block時(shí)靜態(tài)變量值是修改之后的新值
int main(int argc, const char * argv[]) {
    static int testNum = 10;
    void(^testNumBlock)() = ^{
        NSLog(@"%d",testNum);
    };
    testNum = 20;
    testNumBlock();
    return 0;
}
打印結(jié)果:20
  • 在Block中可以直接修改靜態(tài)變量
int main(int argc, const char * argv[]) {
    static int testNum = 10;
    void(^testNumBlock)() = ^{
        testNum = 20;
        NSLog(@"%d",testNum);
    };
    testNumBlock();
    return 0;
}
打印結(jié)果:20
Block作為參數(shù)傳遞
typedef void(^TestBlock)();
NSMutableArray *array;
void test(){    
    int a = 10;
    TestBlock blcok = ^{
        NSLog(@"%d",a);
    };
    [array addObject:blcok];
    NSLog(@"%@",blcok);
}

int main(int argc, const char * argv[]) {

    array = [[NSMutableArray alloc]init];
    test();
    TestBlock blockk = [array lastObject];
    blockk();
    NSLog(@"%@",blockk);
    return 0;
}   
結(jié)果:
在ARC下:
 test2[2423:124143] <__NSMallocBlock__: 0x1004037f0>
 test2[2423:124143] 10
 test2[2423:124143] <__NSMallocBlock__: 0x1004037f0>
在非ARC下:
程序崩潰 
test2[2449:125851] <__NSStackBlock__: 0x7fff5fbff6f8>

1集币、在非ARC下考阱,TestBlock的isa指向NSStackBlock,當(dāng)函數(shù)退出后惠猿,相應(yīng)的堆被銷毀羔砾,block也就不存在了负间,在經(jīng)過(guò)copy或retain之后,對(duì)象的類型從NSStackBlock變?yōu)榱?strong>NSMallocBlock姜凄,在函數(shù)結(jié)束后依然可以訪問(wèn)政溃,在非ARC環(huán)境下,copy或retain了block后一定要在使用后release态秧,不然會(huì)有內(nèi)存泄露董虱,而且泄露點(diǎn)是在系統(tǒng)級(jí),在Instruments里跟不到問(wèn)題觸發(fā)點(diǎn)申鱼,比較上火愤诱。

2、ARC情況下,系統(tǒng)會(huì)將捕獲了外部變量的block進(jìn)行了copy捐友。所以返回類型為NSMallocBlock淫半,在函數(shù)結(jié)束后依然可以訪問(wèn)

如果把blcok中的代碼不再訪問(wèn)變量:

TestBlock blcok = ^{
        NSLog(@"demo");
    };
結(jié)果:
ARC和非ARC得結(jié)果一致
test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
test2[2484:128052] demo
test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
Block作為返回值
  • 非ARC中
- (testBlcok) myTestBlock {
    __block int val = 10;
    return ^{
        NSLog(@"val = %d", val);
    };
}
結(jié)果:Xcode就會(huì)提示報(bào)錯(cuò)Returning block that lives on the local stack

在向外傳遞block的時(shí)候一定也要做到,傳給外面一個(gè)在堆上的匣砖,autorelease的對(duì)象科吭。

- (testBlcok) myTestBlock {
    __block int val = 10;
    return [[^{
        NSLog(@"val = %d", val);
    } copy] autorelease];
}
  • ARC中
- (testBlcok) myTestBlock {
    __block int val = 10;
    return ^{
        NSLog(@"val = %d", val);
    };
}
結(jié)果:正常

在ARC環(huán)境下,當(dāng)block作為參數(shù)返回的時(shí)候猴鲫,block也會(huì)自動(dòng)被移到堆上对人。

Block作為屬性

ARC 和非ARC得聲明一樣

@property (strong, nonatomic) TestBlock *strongBlock;
@property (copy, nonatomic) TestBlock *copyBlock;

Block在MRC及ARC下的內(nèi)存管理

Block在MRC下的內(nèi)存管理
  • 默認(rèn)情況下,Block的內(nèi)存存儲(chǔ)在棧中,不需要開(kāi)發(fā)人員對(duì)其進(jìn)行內(nèi)存管理
- (void)viewDidLoad {
    [super viewDidLoad];
    void(^testBlock)() = ^{
        NSLog(@"------");
    };
    testBlock();
}
結(jié)果:當(dāng)testBlock變量出了作用域,testBlock的內(nèi)存會(huì)被自動(dòng)釋放
  • 在Block的內(nèi)存存儲(chǔ)在棧中時(shí),如果在Block中引用了外面的對(duì)象,不會(huì)對(duì)所引用的對(duì)象進(jìn)行任何操作
- (void)viewDidLoad {
    [super viewDidLoad];
    Student *stu = [[Student alloc]init]; 
    void(^testBlock)() = ^{
        NSLog(@"%@",stu);
    };
    testBlock();
    [stu release];
}
結(jié)果:Student可以正常釋放
  • 如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,這時(shí)需要對(duì)其進(jìn)行release操作來(lái)管理內(nèi)存
- (void)viewDidLoad {
    [super viewDidLoad];

    void(^testBlock)() = ^{
        NSLog(@"testBlock");
    };
    testBlock();
    Block_copy(testBlock);
    Block_release(testBlock);
}
結(jié)果:Block正常釋放
  • 如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行一次retain操作,即使在Block自身調(diào)用了release操作之后,Block也不會(huì)對(duì)所引用的對(duì)象進(jìn)行一次release操作,這時(shí)會(huì)造成內(nèi)存泄漏
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *stu = [[Student alloc]init];
    void(^testBlock)() = ^{
        NSLog(@"%@",stu);
    };
    testBlock();
    Block_copy(testBlock);
    Block_release(testBlock);
    [stu release];
}
結(jié)果:Student無(wú)法正常被釋放,因?yàn)槠湓贐lock中被進(jìn)行了一次retain操作
  • 如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行一次retain操作,為了不對(duì)所引用的對(duì)象進(jìn)行一次retain操作,可以在對(duì)象的前面使用__block來(lái)修飾
- (void)viewDidLoad {
    [super viewDidLoad];
    
    __block Student *stu = [[Student alloc]init];
    void(^testBlock)() = ^{
        NSLog(@"%@",stu);
    };
    testBlock();
    Block_copy(testBlock);
    Block_release(testBlock);
    [stu release];
}
結(jié)果:Student可以正常釋放
  • 如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用
  • 第一種情況
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
@end
   ----------------------------------------------
@implementation Student
-(void)dealloc{
   NSLog(@"%s",__func__);
   Block_release(_testBlock);
   [super dealloc];
}
@end
   ----------------------------------------------
-(void)viewDidLoad {
   [super viewDidLoad];
   
   Student *stu = [[Student alloc]init];
   stu.testBlock = ^{
       NSLog(@"%@",stu);
   };
   stu.testBlock();
   [stu release];
}
結(jié)果:因?yàn)閠estBlock作為Student的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會(huì)對(duì)Student對(duì)象進(jìn)行一次retain操作,導(dǎo)致循環(huán)引用無(wú)法釋放
  • 第二種情況
  @interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
-(void)resetBlock;
@end
    ----------------------------------------------
@implementation Student
-(void)resetBlock{
  
    self.testBlock = ^{
        NSLog(@"%@",self);
    };
}
-(void)dealloc{
    NSLog(@"%s",__func__);

    Block_release(_testBlock);
    [super dealloc];
}
@end
    ----------------------------------------------
-(void)viewDidLoad {
    [super viewDidLoad];
    
    Student *stu = [[Student alloc]init];
    [stu resetBlock];
    [stu release];
}
結(jié)果:Student對(duì)象在這里無(wú)法正常釋放,雖然表面看起來(lái)一個(gè)alloc對(duì)應(yīng)一個(gè)release符合內(nèi)存管理規(guī)則,但是實(shí)際在resetBlock方法實(shí)現(xiàn)中,Block內(nèi)部對(duì)self進(jìn)行了一次retain操作,導(dǎo)致循環(huán)引用無(wú)法釋放
  • 如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是在對(duì)象的前面使用下劃線下劃線block來(lái)修飾,以避免Block對(duì)對(duì)象進(jìn)行retain操作
    • 第一種情況
    @interface Student : NSObject
      @property (nonatomic,copy) void(^testBlock)();
      @end
      ----------------------------------------------
      @implementation Student
      -(void)dealloc{
          NSLog(@"%s",__func__);
          Block_release(_testBlock);
          [super dealloc];
      }
      @end
      ----------------------------------------------
      - (void)viewDidLoad {
          [super viewDidLoad];
           __block Student *stu = [[Student alloc]init];
          stu.testBlock = ^{
              NSLog(@"%@",stu);
          };
          stu.testBlock(); 
          [stu release];
          }
          結(jié)果:Student可以正常釋放
              
    
    • 第二種情況
     @interface Student : NSObject
     @property (nonatomic,copy) void(^testBlock)();
     -(void)resetBlock;
     @end    
     ----------------------------------------------
     @implementation Student
     -(void)resetBlock{
    // 這里為了通用一點(diǎn),可以使用__block typeof(self) stu = self;
     __block Student *stu = self;
     self.testBlock = ^{
         NSLog(@"%@",stu);
         };
     }
     -(void)dealloc{
         NSLog(@"%s",__func__);
         Block_release(_testBlock);
         [super dealloc];
     }
     @end
     ----------------------------------------------
     - (void)viewDidLoad {
         [super viewDidLoad];
          Student *stu = [[Student alloc]init];
         [stu resetBlock];
         [stu release];
     }
     結(jié)果:Student可以正常釋放
    

Block在ARC下的內(nèi)存管理

  • 在ARC默認(rèn)情況下,Block的內(nèi)存存儲(chǔ)在堆中,ARC會(huì)自動(dòng)進(jìn)行內(nèi)存管理,程序員只需要避免循環(huán)引用即可
- (void)viewDidLoad {
    [super viewDidLoad];
    void(^testBlock)() = ^{
        NSLog(@"testBlock");
    };
    testBlock();
}
結(jié)果:當(dāng)Block變量出了作用域,Block的內(nèi)存會(huì)被自動(dòng)釋放
  • 在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行強(qiáng)引用,但是在Block被釋放時(shí)會(huì)自動(dòng)去掉對(duì)該對(duì)象的強(qiáng)引用,所以不會(huì)造成內(nèi)存泄漏
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *stu = [[Student alloc]init];
    void(^testBlock)() = ^{
        NSLog(@"%@",stu);
    };
    testBlock();
}
結(jié)果:Student可以正常釋放
  • 如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用
    • 第一種情況
    
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    @end
       ----------------------------------------------
    @implementation Student
    -(void)dealloc{
       NSLog(@"%s",__func__);
    }
    @end
       ----------------------------------------------
    -(void)viewDidLoad {
       [super viewDidLoad];
        Student *stu = [[Student alloc]init];
        stu.testBlock = ^{
           NSLog(@"%@",stu);
       };
       stu.testBlock();
    }
    結(jié)果:因?yàn)閠estBlock作為Student的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會(huì)對(duì)Person對(duì)象進(jìn)行一次強(qiáng)引用,導(dǎo)致循環(huán)引用無(wú)法釋放
    
    • 第二種情況
     @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    - (void)resetBlock;
    @end
        ----------------------------------------------
        @implementation Student
        - (void)resetBlock
        {
            self.testBlock = ^{
                NSLog(@"------%@", self);
            };
        }
        -(void)dealloc{
            NSLog(@"%s",__func__);
        }
        @end
        ----------------------------------------------
        - (void)viewDidLoad {
            [super viewDidLoad];
            Student *stu = [[Student alloc]init];
            [stu resetBlock];
        }
        結(jié)果:Student對(duì)象在這里無(wú)法正常釋放,在testBlock方法實(shí)現(xiàn)中,Block內(nèi)部對(duì)self進(jìn)行了一次強(qiáng)引用,導(dǎo)致循環(huán)引用無(wú)法釋放
        
    
  • 如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是使用一個(gè)弱引用的指針指向該對(duì)象,然后在Block內(nèi)部使用該弱引用指針來(lái)進(jìn)行操作,這樣避免了Block對(duì)對(duì)象進(jìn)行強(qiáng)引用
  • 第一種情況
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    @end
    ----------------------------------------------
    @implementation Student
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    ----------------------------------------------
    - (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *stu = [[Student alloc]init];
    __weak typeof(stu) weakS = stu;
    
    stu.testBlock = ^{
        NSLog(@"------%@", weakS);
    };
   stu.testBlock();
    
    // Student對(duì)象在這里可以正常被釋放
}

  • 第二種情況
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    -(void)resetBlock;
    @end
        ----------------------------------------------
        @implementation Student
        -(void)resetBlock
        {
            //這里為了通用一點(diǎn),可以使用__weak typeof(self) weakP = self;
            __weak Student *stu = self;
            self.testBlock = ^{
                NSLog(@"------%@", self);
        };
    }
        -(void)dealloc{
            NSLog(@"%s",__func__);
        }
        @end
        ----------------------------------------------
        -(void)viewDidLoad {
            [super viewDidLoad];
            Student *stu = [[Student alloc]init];
            [stu resetBlock];
        }
        結(jié)果:Student可以正常釋放
    
    

注: 關(guān)于下劃線下劃線block關(guān)鍵字在MRC和ARC下的不同

__block在MRC下有兩個(gè)作用

  1. 允許在Block中訪問(wèn)和修改局部變量
  2. 禁止Block對(duì)所引用的對(duì)象進(jìn)行隱式retain操作

__block在ARC下只有一個(gè)作用

  1. 允許在Block中訪問(wèn)和修改局部變量
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拂共,隨后出現(xiàn)的幾起案子牺弄,更是在濱河造成了極大的恐慌,老刑警劉巖宜狐,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件势告,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡肌厨,警方通過(guò)查閱死者的電腦和手機(jī)培慌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柑爸,“玉大人吵护,你說(shuō)我怎么就攤上這事”眵ⅲ” “怎么了馅而?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)譬圣。 經(jīng)常有香客問(wèn)我瓮恭,道長(zhǎng),這世上最難降的妖魔是什么厘熟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任屯蹦,我火速辦了婚禮维哈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘登澜。我一直安慰自己阔挠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布脑蠕。 她就那樣靜靜地躺著购撼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谴仙。 梳的紋絲不亂的頭發(fā)上迂求,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音晃跺,去河邊找鬼揩局。 笑死,一個(gè)胖子當(dāng)著我的面吹牛哼审,可吹牛的內(nèi)容都是我干的谐腰。 我是一名探鬼主播孕豹,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涩盾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了励背?” 一聲冷哼從身側(cè)響起春霍,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叶眉,沒(méi)想到半個(gè)月后址儒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衅疙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年莲趣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饱溢。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喧伞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绩郎,到底是詐尸還是另有隱情潘鲫,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布肋杖,位于F島的核電站溉仑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏状植。R本人自食惡果不足惜浊竟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一怨喘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧振定,春花似錦哲思、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至徘郭,卻和暖如春靠益,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背残揉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工胧后, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抱环。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓壳快,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親镇草。 傳聞我的和親對(duì)象是個(gè)殘疾皇子眶痰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容