iOS-Block探究

作者: FlyElephant | 来源:发表于2017-05-20 17:47 被阅读130次

Blocks是C语言的扩充功能,也可以理解为带有自动变量(局部变量)的匿名函数.C语言中可能用到的变量有自动变量(局部变量),函数的参数,静态变量(静态局部变量),静态全局变量和全局变量.函数中能够传递值的变量有静态变量(静态局部变量),静态全局变量和全局变量.

C++和Objective-C使用类可保持变量值且能够多次持有该变量自身,它会声明持有成员变量的类,由类生成的实例或对象保持该成员变量的值,通过Block可以简化代码,实现自动变量的保存.

基础语法

Block的定义需要定义返回值,Block名字,参数值,简单定义如下:
<pre><code>int (^sumBlock)(int a,int b) = ^(int a,int b) { return a + b; };</code></pre>

调用测试:
<pre><code>int result = sumBlock(10, 20); printf("计算结果:%d\n",result);</code></pre>

通过使用typedef,函数定义变得非常容易理解.

<pre><code>typedef int(^SumBlock)(int a, int b);</code></pre>

<pre><code>` SumBlock block2 = ^(int a,int b) {
return a + b;
};

    int result2 = block2(10, 30);`</code></pre>

block截获变量在执行的时候中会自动保存下来,之后的修改不会影响block中的输出结,如果想在block修改变量的值需要加入__block 修饰.
<pre><code>` int a = 10;
int b = 20;
void (^block3)(void) = ^ {
printf("a = %d b = %d\n",a,b);
};

    a = 30;
    b = 40;
    block3();`</code></pre>

输出结果:

<pre><code>a = 10 b = 20</code></pre>

截获自动变量的方法没发实现对数组的拷贝,数组之后的更改是可以反应到Block中的.

<pre><code>NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3", nil]; void (^block4)(void) = ^ { NSLog(@"数组 = %@",arr[1]); }; arr[1] = @"20"; block4();</code></pre>

输出结果:
<pre><code>数组 = 20</code></pre>

MRC与ARC

估计iOS开发学习中遇到的最多的就是Block了,实际开发中Block遇到的最多的问题就是循环引用,先从一份Block测试题目开始吧.下面的五道题目是同样的Block,大家根据自己的经验来判断下面几个题目在MRC与与ARC下执行结果是否一样:

题目1:
<pre><code>void exampleA() { char a = 'A'; ^{ printf("%cn", a); }(); }</code></pre>

题目2:
<pre><code>`void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}

void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
`</code></pre>

题目3:
<pre><code>`void exampleC_addBlockToArray(NSMutableArray *array) {
[array addObject:^{
printf("Cn");
}];
}

void exampleC() {
NSMutableArray *array = [NSMutableArray array];
exampleC_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}`</code></pre>

题目4:
<pre><code>`typedef void (^dBlock)();

dBlock exampleD_getBlock() {
char d = 'D';
return ^{
printf("%cn", d);
};
}

void exampleD() {
exampleD_getBlock()();
}
`</code></pre>

题目5:
<pre><code>`typedef void (^eBlock)();

eBlock exampleE_getBlock() {
char e = 'E';
void (^block)() = ^{
printf("%cn", e);
};
return block;
}

void exampleE() {
eBlock block = exampleE_getBlock();
block();
}`</code></pre>

答案:
1.无论在MRC还是在ARC情况都能正确执行.
2.ARC情况下执行正确,MRC情况下执行错误.ARC数组添加的Block的类型是NSMallocBlock类型,MRC则将Block按照NSStackBlock处理.
3.无论是MRC还是ARC添加的Block都按照NSGlobalBlock类型处理,所以都能正确执行.
4.只有ARC的情况正确执行,MRC中Block在栈中创建的exampled_getblock返回时已经无效,而且编译器会报错,error: returning block that lives on the local stack.
ARC将在堆中创建一个autorelease的NSMallocBlock类型的Block.
5.只有ARC的情况下正确执行,原因同上.

Block 类型

通过测试题目我们发现了Block有三种类NSConcreteGlobalBlock,NSConcreteStackBlock和NSConcreteMallocBlock,事实上Block还有三种适用于GC的类型NSConcreteWeakBlock,NSConcreteAutoBlock和NSConcreteFinalizingBlock.

1.全局Block:如果block没有访问外界的任何变量或者对象,那么block就是一个全局block:

2.MRC中如果Block访问了外界变量就会变成栈block.栈上的Block,如果其所属的变量作用域结束,该Block就被释放.

3.ARC为了解决栈Block释放出现的问题,会通过编译器默认的会将栈Block进行copy然后变成堆Block,通过引用计数管理Block,延长其生命周期.

以下是三个不同类型的Block展现形式:
<pre><code>`typedef int (^SumBlock)(int a,int b);

@interface ViewController ()

@property (assign, nonatomic) SumBlock stackBlock;

@end`</code></pre>

<pre><code>` void (^globalBlock)() = ^() {
NSLog(@"FlyElephant---全局Block");
};

NSLog(@"FlyElephant---%@",globalBlock);

__weak typeof(self) weakSelf = self;
self.stackBlock = ^int(int a, int b) {
    NSLog(@"FlyElephant---%@",weakSelf.stackBlock);
    return a + b;
};

NSInteger test = self.stackBlock(10,10);
NSLog(@"FlyElephant---%ld---%@",test,self.stackBlock);

NSInteger num = 27;

void (^mallocBlock)() = ^() {
    NSLog(@"FlyElephant---栈Block--%ld",num);
};
NSLog(@"FlyElephant---%@",mallocBlock);`</code></pre>
FlyElephant.png

友情提示:ARC项目中Block使用copy修饰,千万不要用assign修饰,本文为演示,特意用assign对Block进行修饰.

Block 原理

Block是对包含上下文变量的函数的封装,是Objective-C语言对闭包的另外一种形式的封装.Block同时也是一个对象,不过Block内部的构造与一般的对象差别很多.

block-struct.jpg

Block的内存布局是由六个部分组成:
1.isa 指针:所有对象都有该指针,指向Class对象.
2.flags:用于按 bit 位表示一些 block 的附加信息.
3.reserved:保留变量.
4.invoke:最重要的变量,函数指针,指向具体的 block 实现的函数调用地址,至少要接收一个void *型的参数.
5.descriptor:指向结构体的指针,表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针.
6.variables:捕获(capture)过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中.

参考资料:
Objective-C Blocks Quiz
A look inside blocks: Episode 3 (Block_copy)

相关文章

  • iOS-Block探究

    Blocks是C语言的扩充功能,也可以理解为带有自动变量(局部变量)的匿名函数.C语言中可能用到的变量有自动变量(...

  • iOS-Block探究

    问题:1.Block是什么,block当初是为了解决什么样的问题而设计的?2.为什么要用copy修饰Block3....

  • iOS-Block本质

    iOS-Block本质 参考篇:iOS-Block浅谈[https://www.jianshu.com/p/25a...

  • Objective-C的本质(6)——Block本质

    参考:iOS-Block本质iOS底层原理总结 - 探寻block的本质(一)iOS底层原理总结 - 探寻bloc...

  • iOS-block

    一. 查看block内部实现 1.编写block代码void (^DemoBlock)(int, int) = ^...

  • ios-Block

    概述: 能够截取自动变量的匿名函数 指向函数的指针 结构体 oc对象 使用: - 声明 - 定义(变量赋值) - ...

  • iOS-Block

    Block是一种匿名函数,也是一种Objective-C对象。 语法 返回值和参数列表都可以省略 声明 block...

  • iOS-block

    1.相关概念在这篇笔记开始之前,我们需要对以下概念有所了解。1.1 操作系统中的栈和堆注:这里所说的堆和栈与数据结...

  • iOS-Block

    block已经成为我在iOS编写中最为常用的回调方法 , 它简单便捷 , 取代了代理大部分的工作 , 今天整理一下...

  • iOS-block

    一. block的声明、调用、实现 1. block的声明 返回值(^block变量名)(参数); 例如: 2. ...

网友评论

    本文标题:iOS-Block探究

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