iOS底层原理研究所

Block的三种类型

2019-02-22  本文已影响3人  大兵布莱恩特

block有3中类型,可以通过 class方法或者 isa 指针查看具体类型,最终都是继承与NSBlock 的类型 有NSGlobalBlock,NSMallocBlock,NSStackBlock
这三种 block在内存中分配的位置是不同的

image.png

那么什么样的 block 是 NSGlobalBlock?什么样的 block 是 NSMallocBlock ?什么样的 block 是NSStackBlock ?

为了能够准确的研究B lock 内存分配 一下代码是在非ARC环境下测试

image.png

如果 block 内部没有访问auto 变量 那么这个 block 就是 NSGlobalBlock 类型

int height = 10; ///全局变量
void testBlock() {
    
    ///globalBlock
    void(^block1)(void)  = ^ {
        NSLog(@"hello world");
    };
    NSLog(@"%@",[block1 class]);
    ///MallocBlock
    static int age = 10; ///静态变量
    void(^block2)(void) = ^{
        NSLog(@"age = %d",age);
    };
    NSLog(@"%@",[block2 class]);
    ///StackBlock
    NSLog(@"%@",[^{
        NSLog(@"height = %d",height);
    } class]);
    
}

控制器输出打印结果

2019-02-22 10:11:20.325501+0800 Block01[18811:1127199] __NSGlobalBlock__
2019-02-22 10:11:20.325704+0800 Block01[18811:1127199] __NSGlobalBlock__
2019-02-22 10:11:20.325716+0800 Block01[18811:1127199] __NSGlobalBlock__

如果 block 内部访问了 auto 变量 那么这个 block 就是 NSStackBlock

int height = 10; ///全局变量
void testBlock() {
    
    ///__NSGlobalBlock__
    void(^block1)(void)  = ^ {
        NSLog(@"hello world");
    };
    NSLog(@"%@",[block1 class]);
    ///__NSStackBlock__
    int age = 10;  //auto局部变量
    void(^block2)(void) = ^{
        NSLog(@"age = %d",age);
    };
    NSLog(@"%@",[block2 class]);
    ///__NSGlobalBlock__
    NSLog(@"%@",[^{
        NSLog(@"height = %d",height);
    } class]);
    
}
控制器输出打印结果
2019-02-22 10:15:48.642882+0800 Block01[20101:1139963] __NSGlobalBlock__
2019-02-22 10:15:48.643117+0800 Block01[20101:1139963] __NSStackBlock__
2019-02-22 10:15:48.643135+0800 Block01[20101:1139963] __NSGlobalBlock__

我们平时使用 block 的时候 一般都有 copy 操作,这样做是为什么呢?

image.png

2019-02-22 10:29:11.315472+0800 Block01[23997:1178680] NSStackBlock

block是分配在栈内存的 当函数作用域结束的时候 block 就会被内存回收,那么这个时候去调用 block 内部代码 ,这个时候 block 可能已经销毁了 它捕获的 string 变量也可能已经不存在了 这个时候输出 string 的值 就会造成坏内存访问.

image.png

如何能够让 block 一直存在内存中呢 ?
调用[ block copy]就可以将 block 从栈内存拷贝到堆内存 ,在使用 block 的时候就不会出现 block 已经被销毁的情况.

void (^block)(void);
void testBlock() {
    NSString *string = @"dzb";
    block = [^{
        NSLog(@"string = %@",string);
    } copy];
    NSLog(@"%@",[block class]);
}

///控制台输出结果
2019-02-22 10:33:54.178636+0800 Block01[25341:1191775] __NSMallocBlock__
2019-02-22 10:33:57.641726+0800 Block01[25341:1191775] string = dzb

下边的图可以总结出 block 的类型


image.png

在我们平时开发中 定义 block 的地方 和 调用 block 地方可能不在一个地方,这种情况下就需要将 block copy 保证 block 不被销毁

image.png

2019-02-22 10:39:41.851458+0800 Block01[26979:1208502] NSMallocBlock
2019-02-22 10:39:41.851682+0800 Block01[26979:1208502] string = dzb

好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群

QQ20180712-0.png
上一篇下一篇

猜你喜欢

热点阅读