美文网首页
OC中的block

OC中的block

作者: 三国韩信 | 来源:发表于2020-06-20 18:34 被阅读0次

我们都知道block在oc中是很常用的,随处可见,越来越多的原先delegate的实现现在都用block去处理了,可见block的重要性。
那么什么是block呢?block在使用的时候要注意什么呢?
直接show code,看看底层block到底是变成了啥数据结果的

/*
block 被编译成了结构体,根据block捕获变量的情况,会有不同的情况。
struct Block_layout和struct Block_descriptor_1是一定存在的,struct Block_descriptor_2和struct Block_descriptor_3是可选的。
当block是全局block的时候,descriptor_2和descriptor_3是没有的。当block有捕获变量的时候,他两会存在,并会对block和捕获的变量做一下内存的操作。比如会把捕获的变量copy到堆上。
*/
struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor; //
    // imported variables
};

struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
/* block 总共有6中,我们开发常用的有3种,_NSConcreteStackBlock、
_NSConcreteMallocBlock、_NSConcreteGlobalBlock。另外的3种多在系统级别才会被使用到。
*/
void * _NSConcreteStackBlock[32] = { 0 };
void * _NSConcreteMallocBlock[32] = { 0 };
void * _NSConcreteAutoBlock[32] = { 0 };
void * _NSConcreteFinalizingBlock[32] = { 0 };
void * _NSConcreteGlobalBlock[32] = { 0 };
void * _NSConcreteWeakBlockVariable[32] = { 0 };

So,NSGlobalBlock、NSStackBlock、NSMallocBlock这3种又有啥区别呢

在MRC下:
NSGlobalBlock:没有访问(捕获)auto变量(局部变量)的block (数据区)
NSStackBlock:访问(捕获)auto变量(局部变量)的block (栈区)
NSMallocBlock: 对NSStackBlock做了一次copy操作后得到的block。(堆区)

在ARC下:
被强指针引用的block且引用了外部变量,那么会自动做一次copy操作,即把NSStackBlock上的block copy到NSMallocBlock上。即被strong,copy修饰的block且用了外部变量就是NSMallocBlock。

判断捕获对象释放:
NSStackBlock(栈上的block)会对捕获对象进行强引用。(在arc模式下,block作为函数的参数传递,此时的block是NSStackBlock)
NSMallocBlock(堆上的block)会对捕获的对象进行引用,捕获的对象也会被copy到堆空间上。(如果捕获的对象是strong类型的,就会强引用,如果是__weak 修饰的weak类型,就会弱引用)。

typedef void(^MyBlock)(void);
@interface ViewController2 ()
@property(nonatomic,copy)MyBlock myBlock;
@end
@implementation ViewController2

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    int a = 10;
    self.myBlock = ^{   // 被强指针引用且捕获了外部变量,会被copy到堆上,是NSMallocBlock
        a;
        // NSLog(@"%@",self);
        //__NSMallocBlock__
    };
    

    /*block 作为函数的参数来传递,是NSStackBlock,此时即便捕获了外部变量也不会被copy到堆上,
依然是NSStackBlock,也会对捕获的外部变量强引用。*/
    [self getMyhahahahBlock:^{
        // __NSStackBlock__
        a;
        NSLog(@"%@",self);
    }];
}

-(void)getMyhahahahBlock:(MyBlock)block{
    //self.myBlock = block;
    block();
    NSLog(@"%@",block);  
    
}

block访问外部变量有几种方式呢?

  1. __block修饰变量(__block 只能修饰auto的局部变量,不能修饰static变量,也不能修饰全局变量)
  2. static修饰变量
  3. 全局变量

那么__block 做了啥呢?
__block 修饰后,底层会把捕获的局部变量包装成一个对象,通过捕获这个变量来修改局部变量的值。

//__block修饰的变量会被封装成如下的结构体
struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;   // 对捕获的变量copy到内存。
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};
NSMutableArray *array = [NSMutableArray alloc]init];
MyBlock block  = ^{
    [array addObject: @"hello"];  
    /* 这个会报错么?
        被捕获的局部变量不是会被做一次copy操作,copy到堆上么?
        NSMutableArray对象被copy后,不是变成NSArray对象了么?
        NSArray对象能用addObject方法?
    */
    /* 对于这个疑惑,在看了底层源码之后发现,苹果baba不是简单的调了一次[array copy]函数。
        它其实是去堆上开辟了一个和array一样大的内存空间,
        然后把array的内存都搬到新的array上。所以不会存在上面说的情况。
    */
    // 先开辟空间,然后做内存移动。
    struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
    memmove(copy+1, src+1, src->size - sizeof(*src));
}

总结来说,如果是__block修饰的变量,在block内部被修改了,这个过程中存在了3层copy操作:

  • block被从栈copy到了堆上。
  • __block修饰的变量被封装成一个对象,该对象也被copy到堆上了
  • __block修饰的变量被封装成一个对象,该对象内部捕获的变量也被copy到堆上了。

相关文章

  • swift 调用 OC中的block

    OC中声明block; OC中实现block swift中实现

  • iOS原生&JS交互

    OC 获取js中的key OC调用js方法 JS调用OC中不带参数的block JS调用OC中带参数的block ...

  • Swift之闭包

    前言 闭包类似于OC的block,但是比OC的block应用面更广 在OC中block是匿名函数 在swift中函...

  • Swift 之闭包

    闭包 闭包类似于 OC 的 block,但是比 OC 的 block 应用面更广 在 OC 中 block 是匿名...

  • iOS Block本质笔记

    OC中定义block block访问外部参数 OC转C++分析 block的变量捕获机制 为了保证block能够正...

  • OC中block底层原理总结(下)

    关于OC中block的本质结构、block的变量捕获方式请查看OC中block底层原理总结(上)需要先看懂上篇文章...

  • swift 闭包(类似OC中block)

    swift中的闭包 类似OC中block OC中block 回顾创建个HttpTool类.h .m 控制器中 sw...

  • 深入 Block

    Block 前言 Block是OC中对C语言的扩展功能,是一种带有自动变量的匿名函数,Block在OC中的实现,点...

  • swift基础语法(十三)——闭包

    介绍 闭包和OC中的block非常相似OC中的block是匿名的函数Swift中的闭包是一个特殊的函数block和...

  • Swift 闭包(二)

    OC Block 和 Swift 闭包相互调用 我们在 OC 中定义的 Block,在 Swift 中是如何调用的...

网友评论

      本文标题:OC中的block

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