全面了解iOS中的Block

一、什么是Block?

Block是将函数及其执行上下文封装起来的对象。

比如:

NSIntegernum =3;

NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){

        return    n*num;   

 };        

block(2);

二、Block变量截获

1、局部变量截获 是值截获。

 比如:

NSIntegernum =3;

NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){

        return    n*num;    

};        

num =1;

NSLog(@"%zd",block(2));

这里的输出是6而不是2,原因就是对局部变量num的截获是值截获。

同样,在block里如果修改变量num,也是无效的,甚至编译器会报错。

NSMutableArray* arr = [NSMutableArray    arrayWithObjects:@"1",@"2",nil];

void(^block)(void) = ^{

        NSLog(@"%@",arr);//局部变量

        [arr addObject:@"4"];    

};       

 [arr addObject:@"3"];       

 arr =nil;        

block();

打印为1,2,3

局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为nil,对block没有影响,而该对象调用方法会影响

2、局部静态变量截获  是指针截获。

static    NSInteger    num =3;

NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){

        return    n*num;    

};       

 num =1;

NSLog(@"%zd",block(2));

输出为2,意味着num = 1这里的修改num值是有效的,即是指针截获。

同样,在block里去修改变量m,也是有效的。

三、Block的几种形式

分为全局Block(_NSConcreteGlobalBlock)、栈Block(_NSConcreteStackBlock)、堆Block(_NSConcreteMallocBlock)三种形式

其中栈Block存储在栈(stack)区,堆Block存储在堆(heap)区,全局Block存储在已初始化数据(.data)区

1、不使用外部变量的block是全局block

比如:

NSLog(@"%@",[^{

        NSLog(@"globalBlock");

}class]);

输出:

__NSGlobalBlock__

2、使用外部变量并且未进行copy操作的block是栈block

比如:

NSInteger    num =10;

NSLog(@"%@",[^{

        NSLog(@"stackBlock:%zd",num);    

}class]);

输出:

__NSStackBlock__

日常开发常用于这种情况:

[self    testWithBlock:^{

        NSLog(@"%@",self);

}];

- (void)testWithBlock:(dispatch_block_t)block {    

        block();

        NSLog(@"%@",[block    class]);

}

3、对栈block进行copy操作,就是堆block,而对全局block进行copy,仍是全局block

比如堆1中的全局进行copy操作即赋值:

void(^globalBlock)(void) = ^{

        NSLog(@"globalBlock");    

};

NSLog(@"%@",[globalBlock    class]);

输出:

__NSGlobalBlock__

仍是全局block

而对2中的栈block进行赋值操作:

NSInteger    num =10;

void(^mallocBlock)(void) = ^{

        NSLog(@"stackBlock:%zd",num);    

};

NSLog(@"%@",[mallocBlock    class]);

输出:

__NSMallocBlock__

对栈blockcopy之后,并不代表着栈block就消失了,左边的mallock是堆block,右边被copy的仍是栈block

比如:

[self    testWithBlock:^{

NSLog(@"%@",self);

}];

- (void)testWithBlock:(dispatch_block_t)block{    

        block();        

        dispatch_block_t tempBlock = block;

        NSLog(@"%@,%@",[block    class],[tempBlock    class]);

}

输出:

__NSStackBlock__  ,   __NSMallocBlock__

即如果对栈Block进行copy,将会copy到堆区,对堆Block进行copy,将会增加引用计数,对全局Block进行copy,因为是已经初始化的,所以什么也不做。

另外,__block变量在copy时,由于__forwarding的存在,栈上的__forwarding指针会指向堆上的__forwarding变量,而堆上的__forwarding指针指向其自身,所以,如果对__block的修改,实际上是在修改堆上的__block变量。

即__forwarding指针存在的意义就是,无论在任何内存位置,  都可以顺利地访问同一个__block变量。

另外由于block捕获的__block修饰的变量会去持有变量,那么如果用__block修饰self,且self持有block,并且block内部使用到__block修饰的self时,就会造成多循环引用,即self持有block,block 持有__block变量,而__block变量持有self,造成内存泄漏。

比如:

__block    typeof(self) weakSelf =self;        

_testBlock = ^{

        NSLog(@"%@",weakSelf);    

};       

 _testBlock();

如果要解决这种循环引用,可以主动断开__block变量对self的持有,即在block内部使用完weakself后,将其置为nil,但这种方式有个问题,如果block一直不被调用,那么循环引用将一直存在。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Block的底层基本结构 通过clang命令查看编译器是如何实现Block的,在终端输入clang -rewrit...
    小凉介阅读 20,490评论 22 96
  • Block概要 Block:带有自动变量的匿名函数。 匿名函数:没有函数名的函数,一对{}包裹的内容是匿名函数的作...
    zweic阅读 552评论 0 2
  • 在介绍Block之前通过一个简单的应用场景认识下Block 场景描述如下:TableView上面有多个Custom...
    黑_白_灰阅读 1,484评论 4 29
  • Block作为Objective-C中闭包的实现在iOS开发中占有非常重要的地位,尤其是作为回调(callback...
    NotFunGuy阅读 868评论 0 4
  • 每个同学都是“寒窗苦读了十二载”才考上了大学。或许自己所上的大学并不是自己心心念念的那一所,但不管怎么说,这是自己...
    王诗娴阅读 690评论 1 0

友情链接更多精彩内容