iOS-Block探究

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

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

基础语法

Block的定义需要定义返回值,Block名字,参数值,简单定义如下:

int (^sumBlock)(int a,int b) = ^(int a,int b) {
            return a + b;
        };

调用测试:

int result = sumBlock(10, 20);
        printf("计算结果:%d\n",result);

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

typedef int(^SumBlock)(int a, int b);
SumBlock block2 = ^(int a,int b) {
            return a + b;
        };
        
        int result2 = block2(10, 30);

block截获变量在执行的时候中会自动保存下来,之后的修改不会影响block中的输出结,如果想在block修改变量的值需要加入__block 修饰.

int a = 10;
        int b = 20;
        void (^block3)(void) = ^ {
            printf("a = %d  b = %d\n",a,b);
        };
        
        a = 30;
        b = 40;
        block3();

输出结果:

a = 10  b = 20

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

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

输出结果:

数组 = 20

MRC与ARC

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

题目1:

void exampleA() {
  char a = 'A';
  ^{
    printf("%cn", a);
  }();
}

题目2:

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();
}

题目3:

void exampleC_addBlockToArray(NSMutableArray *array) {
  [array addObject:^{
    printf("Cn");
  }];
}

void exampleC() {
  NSMutableArray *array = [NSMutableArray array];
  exampleC_addBlockToArray(array);
  void (^block)() = [array objectAtIndex:0];
  block();
}

题目4:

typedef void (^dBlock)();

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

void exampleD() {
  exampleD_getBlock()();
}

题目5:

typedef void (^eBlock)();

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

void exampleE() {
  eBlock block = exampleE_getBlock();
  block();
}

答案:
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展现形式:

typedef int (^SumBlock)(int a,int b);

@interface ViewController ()

@property (assign, nonatomic) SumBlock stackBlock;

@end
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);
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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容