block

作者: 一个半吊子工程师 | 来源:发表于2020-09-11 11:09 被阅读0次
  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block是封装函数及其上下文的OC对象

block 语法:

^ 返回值类型 参数列表 表达式 

^ (NSString *) (NSString *bookId){}

省略返回值写法:

^(void){};

省略返回值及参数写法:

^{};

Block定义声明使用

//声明
typedef void (^Block)();
typedef int (^MyBlock)(int , int);
typedef void(^ConfirmBlock)(BOOL isOK);
typedef void(^AlertBlock)(NSInteger alertTag);

//定义属性
@property (nonatomic,copy) MyBlock myBlockOne;

//使用
self.myBlockOne = ^int (int ,int){
    //TODO
}
image.png

Block的类型

block 有三种类型:

  • 全局块(_NSConcreteGlobalBlock)

  • 堆块(_NSConcreteMallocBlock)

  • 栈块(_NSConcreteStackBlock)
    存储区域如下:


    存储区域
  • 全局块存在于全局内存中,相当于单例;

  • 栈块存在于栈内存中,超出其作用域则马上被销毁;

  • 堆块存在于堆内存中,是一个带引用计数的对象,需要自行管理内存

A. 全局Block(_NSConcreteGlobalBlock)

  • 数据区域(.data区)
  • 在block内部不会访问任何外部变量,或者只使用静态变量和全局变量。
//不会访问任何外部变量
- (void)GlobalBlock{
    void (^blk)(void) = ^ {
    };
    blk();
    NSLog(@"blk>>>>:%@",blk);
}
image.png
//只使用静态变量和全局变量
/** 全局变量 */
int global_count = 10;
/** 静态全局变量 */
static int static_global_count = 10;

int main(int argc, const char * argv[]) {
    
    /** 静态局部变量 */
    static int static_count = 10;
    
    void (^block)(void) = ^ {
        global_count = 11;
        static_global_count = 11;
        static_count = 11;
    };
    
    block();
    return 0;
}
image.png

B. 堆Block(_NSConcreteMallocBlock)

  • 位于堆区
  • 在Block内部使用局部变量或者OC的属性,并且赋值给强引用或者Copy修饰的变量
- (void)MallocBlock{
    int val = 10;
    void (^blk2)(void) = ^ {
        NSLog(@">>>>:%@",@(val));
    };
    blk2();
    NSLog(@">>>>:%@",blk2);
}
image.png

C.栈Block(_NSConcreteStackBlock)

  • 位于栈区,栈内有效,出栈后销毁。
  • 与MallocBlock一样,可以在内部使用局部变量或者OC的属性。但是不能赋值给强引用或者Copy修饰的变量
  • 在ARC环境下,捕获局部变量、成员变量,且没有被强引用的block都是_NSConcreteStackBlock
捕获局部变量
- (void)StackBlock{
   /** 局部变量 */
    int count = 10;
    /** 
        执行 
        或者使用void (^__weak block)(void)来指向block
    */
    ^{
        NSLog(@"%d", count);
    }();
    // 打印Block对象
    NSLog(@"blk25>>>>:%@", ^{
        NSLog(@"%d", count);
    });
}
image.png
捕获成员变量
@interface ViewController ()

@property (nonatomic, assign) int count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];    
    NSLog(@"Block对象:%@", ^{
        NSLog(@"%d", self.count);
    });

    ^{
        NSLog(@"%d", self.count);
    }();
}
@end

但是在ARC下,很少存在栈上的block。因为很多情况下ARC会主动帮你把block copy一次。
举个例子:

__block int static_k = 3;
void (^myBlock)(void) = ^{
    static_k++;
};
myBlock();
NSLog(@"%@",myBlock);

在MRC下,输出结果为__NSStackBlock栈。
在ARC下,输出结果为__NSMallocBlock堆。

因为在block在捕获外部变量时ARC会自动帮我们把栈上的block copy到堆上,其中还包括下面的情况系统也会主动copy一次。

以下情况会被保存在堆上:

1.手动调用copy
2.Block是函数的返回值
3.Block被强引用,Block被赋值给__strong或者id类型
4.调用系统API入参中含有usingBlcok的方法

但并不是说在ARC上就没有栈上的block。其中当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。其中GCD等系统方法中本身带usingBlock的方法,不需要处理。

三种block的拷贝效果

不多说,直接上表格:

Block的类 副本源的配置存储域 赋值效果
_NSConcreteGlobalBlock 数据区 什么也不做
_NSConcreteStackBlock 从栈拷贝到堆
_NSConcreteMallocBlock 引用计数增加

数据区中的_NSConcreteGlobalBlock执行拷贝操作什么也不做的原因是不需要,因为_NSConcreteGlobalBlock在进程结束后才销毁,已经是广域变量了。

Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛

  • 动画
  • 多线程
  • 集合遍历
  • 网络请求回调

相关文章

  • iOS开发之Block原理探究

    Block概述 Block本质 Block调用 Block分类 Block循环引用 Block原理探究 Block...

  • block的使用

    定义block 返回类型 (^block名称)(参数) = ^(){block内容}; 调用block block...

  • Block 02 - __block

    Block 02 - __block __block 的作用 __block 可以解决 Block 内部无法修改 ...

  • iOS面试之Block大全

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS面试之Block模块

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • iOS开发block是用copy修饰还是strong

    Block分为全局Block、堆Block和栈Block1、在定义block没有引用外部变量的时候,block为全...

  • block 初探

    全局block, 栈block, 堆block

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

网友评论

      本文标题:block

      本文链接:https://www.haomeiwen.com/subject/hozjsktx.html