Block理解

问题缘由:
1、下面这段代码有问题么?如果没有,为什么?


- (void)blockDemo6
{
    __weak ViewController *weakSelf = self;
    void(^task)() = ^{
        [weakSelf callFunction];
    };
   task();
}

- (void)callFunction{
    [self callFunction2];
}

- (void)callFunction2{
    
}

答案:没有
原因:block本质上也是一个对象,对象对其外部变量 是拷贝和引用在block内部已经处理好,block内部调用外部函数,相当于直接调用 self 对象 调用其外部函数指针,这个时候,如果self 释放,函数将不执行

2、block套用block有问题么

- (void)blockDemo6
{
    __weak ViewController *weakSelf = self;
    void(^task)(void) = ^{
        
        void(^task2)(void) = ^{
            [weakSelf callFunction];
            
        };
        [weakSelf callFunction];
        task2();
    };
    task();
}

- (void)callFunction{
    
}

答案:没问题 内部调用最外层的WeakSelf,不要造成内存问题,没有任何问题

block小结

block组成结构
struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void *);
};
struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

Block_layout 为block组成结构
如图了解到:
void isa; block 对象也有指向对象本身的 isa指针
void (
invoke)(void *, ...) block对象需要调用的函数指针
struct Block_descriptor *descriptor; block对象对外部变量的引用和拷贝

block 分类

NSConcreteGlobalBlock 不捕获外部变量的block
NSConcreteMallocBlock 执行过copy操作的block
NSConcreteStackBlock MRC 捕获外部变量没有执行过copy操作的block
code分析

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

这段代码在 MRC 和 ARC 的情况下都能正确执行。
因为 exampleA 的函数栈,在 block 执行完之前,并不会 pop,所以,无论函数中的 block 是在 stack 或是 heap 中,都能够被正确执行

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

这段代码只有在 ARC 的情况下才能正确执行。
在 MRC 的情况下,block 分配在栈上,在 exampleB_addBlockToArray 返回之后,函数栈被弹出,这个 block 的地址就不再合法了。
在 ARC 的情况下,block 将会被拷贝到堆中,可以合法使用。

外部变量值拷贝与值引用

Block理解_第1张图片
image.png

code 分析:

//StrM 浅拷贝 地址拷贝  指向对象内容一样
- (void)blockDemo4
{
    NSMutableArray *strM= [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];
    NSLog(@"strM指针在栈区的地址%p strM内容在堆区的地址%p",&strM,strM);
    
    void (^block)() = ^ {
        
        // 只修改strM指向堆区的内容(字符串);没有修改strM指针所在的地址
        //(存放字符串的堆区地址没变,只是&strM变成堆区地址);
        [strM addObject:@"4"];
        NSLog(@"strM指针拷贝到堆区的新地址%p strM内容在堆区的地址%p",&strM,strM);
    };
    
    block();
    NSLog(@"%@",block);
}

//值拷贝
- (void)blockDemo11
{
    int num = 10;
    
    void(^task)() = ^{
        // 栈区地址比堆区地址大
        NSLog(@"堆区地址 ===> %p-%d",&num,num);
    };
    
    num = 20;
    task();
}

//值引用
/*  指针地址一样
 2019-01-17 12:13:59.728070+0800 TestIF[48445:7410873] 栈区地址 ===> 0x7ffee3a5e9d8-20
 2019-01-17 12:13:59.728202+0800 TestIF[48445:7410873] 堆区地址 ===> 0x7ffee3a5e9d8-20
 */
- (void)blockDemo12
{
   __block int num = 10;
    
    void(^task)() = ^{
        // 栈区地址比堆区地址大
        NSLog(@"堆区地址 ===> %p-%d",&num,num);
    };
    
    num = 20;
    NSLog(@"栈区地址 ===> %p-%d",&num,num);
    task();
}

参考地址:
原理参考:https://blog.devtang.com/2013/07/28/a-look-inside-blocks/
Objective-C Blocks 小测验 http://www.futantan.com/2016/03/10/objective-c-blocks-quiz/
IOS工程中混合使用ARC与MRC https://www.jianshu.com/p/a528efd82f11
开发 - Block相关的面试题 https://www.jianshu.com/p/fd40a18369cf

你可能感兴趣的:(Block理解)