iOS--Block

Block都继承自NSBlock,并最终继承与NSObject,所以Block有 isa 指针,本质是一个封装了函数调用以及函数调用环境的OC对象。
Block调用实际上是函数调用。

block底层结构

Block分为

  1. _NSConcreteStackBlock 栈Block
  2. _NSConcreteGlobalBlock 全局Block
  3. _NSConcreteMallocBlock 堆Block


    block类型
//没有访问 auto 变量, Global block
    void (^block1)(void) = ^{
        NSLog(@"----------block1");
    };
    
    //访问 auto 变量, Stack block (关闭ARC)
    int number = 12;
    void (^block2)(void) = ^{
        NSLog(@"----------block2, %d", number);
    };
    
    // 栈 block 调用了 copy
    [block2 copy];
block在栈上不定时会被销毁,为了保持栈,调用copy或者对栈属性设置copy关键字,将栈block移到堆内存保存,注意MRC环境下堆内存的release。

Block分类与内存位置

Block分类与内存位置

在 ARC 环境下,编译器会根据情況自动将栈上的 block 复制到堆上,比如以下情况:

  • block 作为函数返回值时
  • 将 block 赋值给 __strong 指针时(block 的 property 设置为 copy, 对应的是这一条,对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。)
  • block 作为 Cocoa API 中方法名含有 using Block 的方法参数时
  • block 作为 GCD API 的方法参数时

Block 的 copy 操作

Block 的 copy 操作

Block变量捕获(capture)

为了保证 Block内部能够正常访问外部的变量,block 有个变量捕获机制

1. 对于 基本数据类型 的 局部变量 截获变量的 值,block增加一个变量存储这个局部变量的 值。

局部变量定义 默认是 auto :自动变量 ,如 int number 6; 实际是 auto int number = 6;,因为默认是 auto,所以一般都是省略的。auto只存在于局部变量,自动变量离开作用域就销毁,为了保证block在执行时依然能获取到值,block只能先捕获自动变量的值。

//基本类型的局部变量截获
- (void)methond0{
    int number = 6;
    int (^Blockt)(int) = ^int(int num) {
        return num * number;
    };
    number = 1;
    NSLog(@"基本类型的局部变量截获:%d", Blockt(2));
}
输出结果:基本类型的局部变量截获:12
2. 对于 静态局部变量 以 指针形式 截获。

因为静态变量常驻内存的,block在使用静态局部变量时只要保存一个指向静态局部变量地址的指针,在需要使用这个变量的时候,通过指针指向的地址,获取最新的值。

//基本类型的静态局部变量截获
- (void)methond1{
    static int number = 6;
    int (^Blockt)(int) = ^int(int num) {
        return num * number;
    };
    number = 1;
    NSLog(@"基本类型的静态局部变量截获:%d", Blockt(2));
}
输出结果:基本类型的静态局部变量截获:2
3. 对于对象类型的局部变量(auto变量) 连同所有权修饰 一起截获。
对象类型的局部变量(auto变量)

特别的对于 self,self 是对象在调用函数的时候传入的参数,不同对象调用同一个函数,self参数指代不同的对象。

void (^block)(void) = ^{
        NSLog(@"--------%p", self);
    };
self 是局部变量,会被捕获

对于类的属性,block会捕获这个类,再去访问这个类的属性,如self有一个 name 属性,下面block依然会捕获self,然后通过self去访问name属性:

void (^block)(void) = ^{
        NSLog(@"--------%p", _name);
    };
4. 不截获全局变量(包括静态全局变量)。

因为block在任何位置任何时间都可以直接访问到全局变量。

总结:局部变量会捕获,全局变量不会捕获

----------------------------------------------------------------------------------

__block 修饰符

一般情况下,对被截获变量进行 赋值 操作需要添加 __block 修饰符

{
    NSMutableArray *array = [NSMutableArray array];   
    void (^Block)(void) = ^ {
        //对数组添加元素,使用数组,不需要__block 修饰符
        [array addObject:@1];    
    };
    Block();
}
    __block NSMutableArray *array = nil;
    void (^Block)(void) = ^ {
      //对array初始化赋值,需要__block 修饰符
        array = [NSMutableArray array];   
    };
    Block();
  • 需要__block 修饰符:对局部变量(包括基本数据类型和对象类型)进行赋值时。
- (void)methond0{
    __block int number = 6;
    int (^Blockt)(int) = ^int(int num) {
        return num * number;
    };
    number = 1;
    NSLog(@"__block修饰的基本类型的局部变量截获:%d", Blockt(2));
}
输出结果:__block修饰的基本类型的局部变量截获:2

__block 修饰的变量最终变成了对象。

  • 不需要__block 修饰符:对静态局部变量,静态全局变量,全局变量进行赋值。


    image.png
image.png

__block的内存管理

__block的内存管理

block从堆中移除

栈上用__block修饰符修饰的变量在copy之后,在堆上分配了一个与原来变量一样的内存,并且栈上变量的forwarding指针指向堆上block变量的地址;堆上变量forwarding指针也指向自己block变量的地址;
也就是说,在__block变量进行copy之后会在堆上产生一个相同的变量,forwarding指针都指向堆上__block变量。


image.png

__forwarding存在的意义:
不论在内存的任何位置,都可以顺利的访问同一个__block 变量。
如果对__block 变量不copy,操作的就是栈上的__block 变量;
如果发生了copy,无论操作的是栈上、还是堆上__block 变量,都是使用的堆上__block 变量。


image.png

栈上的Block在变量的作用域结束之后,或者说栈上的函数退出之后销毁。

在MRC环境下,对Block进行copy之后,是否会引起内存泄漏?是的。
随着栈上block和__Block变量作用域的结束,该block和__Block变量会销毁,但是堆上的block或者__block变量没有其它成员变量去指向它,引起内存泄漏。

Block笔试题:

@property(nonatomic, copy)int(^blk)(int);

{
    __block int m = 10;
    _blk = ^int(int num){
       return num * m;
    };
    
    m = 6;
    
    [self executeBlock];
}

- (void)executeBlock
{
    int result = _blk(4);
    NSLog(@"result is %d", res);
}
> result is 24

由于multiplier被__block修饰,在clang编译之后是一个结构体,multiplier是 该结构体的一个成员变量:
在multiplier = 6时,是结构体中forwarding指向的multiplier成员变量赋值为6;
(multiplier.__forwarding->multiplier) = 6;
如果_blk没有进行copy操作,修改的就是栈上的__block变量;反之修改的是堆上__block变量;

__block说明符在MRC和ARC下的区别:
__block说明符的作用是 修饰变量后 可以让变量成为对象,MRC下,__block修饰的变量成为对象后,被block使用后没有强引用的关系,而ARC下有block对__block修饰的变量有强引用的关系;

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

推荐阅读更多精彩内容

  • 前面我们说了weak和strong的一些作用,自然我们接下来就要聊一下block。Block在我们项目中绝对会用到...
    Harry__Li阅读 610评论 0 0
  • 假设我们熟悉代理递值的话,对代理我们可能又爱有恨!我们先建立模型A页面 push B页面,如果把A页面的值传递到B...
    郭伟_技术与产品阅读 208评论 0 0
  • Block在开发中常用的,要想解决Block在开发中遇到的问题,我们需要了解Block的本质、截获变量的特性、__...
    字节码阅读 524评论 0 2
  • Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功...
    iOSunRain阅读 152评论 0 0
  • 1. Block是啥? 答:Block是将函数及其执行上下文封装起来的对象。 使用终端编译.m内容: 编译之后会生...
    DoBetter1阅读 270评论 0 1