iOS-底层原理(8)-block-本质,类型,copy属性详解

1.Block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block的底层结构如下图所示
image.png
image.png

代码佐证

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

// block底层结构体
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
};

// 1.block结构体
void test1() {
    int age = 10;
    
    // 定义block
    void(^block)(int, int) = ^(int a, int b){
        NSLog(@"this is a block! -- %d", age);
        NSLog(@"a = %d, b = %d",a,b);
    };
    
    struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
    block(100,200);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1. block结构体
        test1();
    }
    return 0;
}
image.png
2.block的变量捕获(capture)
  • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
image.png
  • auto:自动变量,离开作用域就销毁

代码例子如下:

// 捕获变量类型
void (^block)(void);

void blockTest() {
    int age = 10;
    static int height = 10;
    
    block = ^ {
        NSLog(@"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        blockTest();
        block();
    }
    return 0;
}

运行结果

image.png
Block的本质 - NSObject对象
image.png
3.block的类型
  • block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
  • __NSGlobalBlock__ (_NSConcreteGlobalBlock )
  • __NSStackBlock__ ( _NSConcreteStackBlock )
  • __NSMallocBlock__ ( _NSConcreteMallocBlock )
image.png
image.png
  • 每一种类型的block调用copy后的结果如下所示
image.png

代码例子如下

  • 先将环境切换为MRC,setting -> OC Automitic RF -> NO
// 4.block的类型
int weight = 100;
void (^block)(void);

void blockClassType() {
    // 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
    static int age = 10;
    // 局部static变量
    int height = 10;
    
    // Global:没有访问auto变量
    void(^block1)(void) = ^ {
        NSLog(@"block1");
    };
    // Globa2:访问static变量
    void(^block2)(void) = ^ {
        NSLog(@"block2 - age = %d",age);
    };
    // Globa3:访问全局变量
    void(^block3)(void) = ^ {
        NSLog(@"block3 - weight = %d",weight);
    };
    
    // Stack:访问了auto变量
    void (^block4)(void) = ^{
        NSLog(@"block4 - height = %d", height);
    };
    
    // NSMallocBlock - 对StackBlock做copy操作
    block = [^{
        NSLog(@"block---------%d", height);
    } copy];
    [block release];
    
    NSLog(@"%@ %@ %@ %@ %@",
          [block1 class],
          [block2 class],
          [block3 class],
          [block4 class],
          [block class]
    );
}

运行结果如下

image.png
4. 数据存储位置

代码例子如下

// 5.数据存储位置
int age = 100;
void dataLocationTest() {
    int a = 10;
    
    NSLog(@"数据段:age %p", &age);
    NSLog(@"栈:a %p", &a);
    NSLog(@"堆:obj %p", [[NSObject alloc] init]);
    NSLog(@"数据段:class %p", [Person class]);
}

打印结果

image.png
5.block的copy

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

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时

代码例子如下

  • 1.block作为函数返回值时
// 定义一个block
typedef void(^CSBlock)(void);

// 定义一个返回blcok的函数
CSBlock myBlock() {
    int age = 10;
    return ^{
        NSLog(@"age = %d",age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1.block作为函数返回值
        CSBlock block = myBlock();
        block();
        NSLog(@"%@",[block class]);
    }
    return 0;
}

运行结果

image.png
  • 2.将block赋值给__strong指针时
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 2.block有被强指针引用
        // ARC - 环境下 block - __NSMallocBlock__
        // MRC - 环境下 block - __NSStackBlock__
        int age = 10;
        CSBlock block = ^{
            NSLog(@"---------%d", age);
        };
        NSLog(@"%@", [block class]);
    }
    return 0;
}

打印结果

ARC.png
MRC.png
  • 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
// 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
NSArray *array = [NSArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            
}];
  • 4.block作为GCD API的方法参数时
// 4.block作为GCD API的方法参数时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            
});
block建议写法
  • MRC下block属性的建议写法
    @property (copy, nonatomic) void (^block)(void);

  • ARC下block属性的建议写法
    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);


本文参考借鉴MJ的教程视频,非常感谢.


项目演示代码如下
iOS_block_本质
iOS-block-copy


更多block相关文章

iOS-copy底层原理之auto变量

iOS-block底层原理详解之__block属性

iOS-block底层原理之循环引用详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 30,007评论 8 265
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,887评论 1 32
  • 面试题 block的原理是怎样的?本质是什么? __block的作用是什么?有什么使用注意点? block的属性修...
    xx_cc阅读 14,677评论 10 77
  • 抒情篇 坐在窗前无聊的刷朋友圈看别人的新年活动,突然收到你接受了他表白和他在一起了的消息,突然感觉好激动,但也觉得...
    octor阅读 3,034评论 0 0
  • 美丽的花山公园,是淄博中心城区仅有的一处有山有水的公园,是淄博高新区政府历经2年之久、投资1.5亿多进行了重...
    我爱执着阅读 2,726评论 0 0

友情链接更多精彩内容