浅谈iOS---block

什么是Block?

blocks是C语言的扩充功能。用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的匿名函数。

block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block是封装函数及其上下文的OC对象

block捕获变量

int age=10;
void (^Block)(void) = ^{
    NSLog(@"age:%d",age);
};
age = 20;
Block();

输出值为 age:10
原因:创建block的时候,已经把age的值存储在里面了。

auto int age = 10;
static int num = 25;
void (^Block)(void) = ^{
    NSLog(@"age:%d,num:%d",age,num);
};
age = 20;
num = 11;
Block();

输出结果为:age:10,num:11
愿意:auto变量block访问方式是值传递,static变量block访问方式是指针传递(还是不太理解)

为什么block对auto和static变量捕获有差异?

auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;static变量一直保存在内存中,指针访问即可

block对全局变量的捕获方式是?
block不需要对全局变量捕获,都是直接采用取值的

为什么局部变量需要捕获?
考虑作用域的问题,需要跨函数访问,就需要捕获

block的变量捕获(capture)
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
在这里插入图片描述
block里访问self是否会捕获?
会,self是当调用block函数的参数,参数是局部变量,self指向调用者

block里访问成员变量是否会捕获?
会,成员变量的访问其实是self->xx,先捕获self,再通过self访问里面的成员变量

block语法

^返回值 (参数列表)表达式
例如:

^int (int count){return count + 1;}

block变量

Block变量类似于函数指针
声明Block类型变量仅仅是将声明函数指针类型变量的“*”变为^

int (^blk)(int)

block变量与c语言变量完全相同,可以作为以下用途:

自动变量
函数参数
静态变量
静态全局变量
全局变量

block类型

block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

__NSGlobalBlock __ ( _NSConcreteGlobalBlock ) 对象存储在数据区
__NSStackBlock __ ( _NSConcreteStackBlock ) 对象存储在栈区
__NSMallocBlock __ ( _NSConcreteMallocBlock )对象存储在堆区

注意:

  • 堆:动态分配内存,需要程序员自己申请,程序员自己管理
  • 栈:自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况
    在这里插入图片描述
void (^block1)(void) = ^{
    NSLog(@"block1");
};
NSLog(@"%@",[block1 class]);
NSLog(@"%@",[[block1 class] superclass]);
NSLog(@"%@",[[[block1 class] superclass] superclass]);
NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);
NSLog(@"%@",[[[[[block1 class] superclass] superclass] superclass] superclass]);

输出结果:
NSGlobalBlock
__NSGlobalBlock
NSBlock
NSObject
null

如何判断block是哪种类型?

  1. 没有访问auto变量的block是__NSGlobalBlock __ ,放在数据段
  2. 访问了auto变量的block是__NSStackBlock __
  3. [__NSStackBlock __ copy]操作就变成了__NSMallocBlock __

对每种类型block调用copy操作后是什么结果?

  1. __NSGlobalBlock __ 调用copy操作后,什么也不做
  2. __NSStackBlock __ 调用copy操作后,复制效果是:从栈复制到堆;副本存储位置是堆
  3. __NSMallocBlock __ 调用copy操作后,复制效果是:引用计数增加;副本存储位置是堆

NSGlobalBlock
如果一个 block 没有访问外部局部变量,或者访问的是全局变量,或者静态局部变量,此时的 block 就是一个全局 block ,并且数据存储在全局区。

	//block1没有引用到局部变量
    int a = 10;
    void (^block)(void) = ^{
         NSLog(@"hello world");
    };
    NSLog(@"block:%@", block);

	//	block2中引入的是静态变量
    static int a1 = 20;
    void (^block)(void) = ^{
        NSLog(@"hello - %d",a1);
    };

    NSLog(@"block:%@", block);

在这里插入图片描述
在这里插入图片描述

NSStackBlock
在Block名前面加个__weak就是栈区block

    __block int a = 10;
    static int a1 = 20;
    void (^__weak block)(void) = ^{
        NSLog(@"hello - %d",a);
        NSLog(@"hello - %d",a1);
    };
    NSLog(@"block:%@", block);

在这里插入图片描述

NSMallocBlock
当Block里有引用到局部变量时为堆区block

    	int a = 10;
        void (^block1)(void) = ^{
            NSLog(@"%d",a);
        };

        NSLog(@"block1:%@", block1);
    
        __block int b = 10;
        void (^block2)(void) = ^{
            NSLog(@"%d",b);
        };

        NSLog(@"block2:%@", block2);

在这里插入图片描述
通过结果我们看到,首先block的地址是在栈区,而block1的地址是在堆区,而栈block引用的变量a的地址并没有变化,而堆block1引用的变量b的地址也相应变成了堆区0x283e0b1b8,并且后面使用的b的地址都是堆区上的。

总结:栈block存放在栈区,对局部变量引用只拷贝局部变量的地址,而堆block存放在堆区,并且直接将局部变量拷贝了一份到堆空间。

	NSObject *objc = [NSObject new];
    NSLog(@"%@---%ld",objc, CFGetRetainCount((__bridge CFTypeRef)(objc)));// 1
    // block 底层源码
    // 捕获 + 1
    // 堆区block
    // 栈 - 内存 -> 堆  + 1
    void(^strongBlock)(void) = ^{ // 1 - block -> objc 捕获 + 1 = 2
        NSLog(@"%@---%ld",objc, CFGetRetainCount((__bridge CFTypeRef)(objc)));
    };
    strongBlock();

    void(^__weak weakBlock)(void) = ^{ // + 1
        NSLog(@"%@---%ld",objc, CFGetRetainCount((__bridge CFTypeRef)(objc)));
    };
    weakBlock();
    
    void(^mallocBlock)(void) = [weakBlock copy];
    mallocBlock();

运行结果:
在这里插入图片描述
奇怪为什么堆区block里面的对象引用计数加2呢?而后面的mallocBlock只加1呢?

首先objc在strongBlock里面必然会拷贝一份到堆区,所以会加1,但是他是从当前函数的栈区拷贝吗?并不是,对于堆区Block一开始编译时是栈block这时候objc对象地址拷贝了一份引用计数加1,后面从栈block变成堆block,又拷贝了一份引用计数又加1,所以这时候是3,weakBlock是栈block仅拷贝了一份,所以引用计数加1,这时候是4,mallocBlock从weakblock拷贝了一份,所以引用计数再加1,这时候是5,相当于strongBlock
= weakblock + void(^mallocBlock)(void) = [weakBlock copy];

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值