iOS Block 部分二

主要讲解 Block 的分类和变量捕获的强弱引用;

Block部分一
Block部分二
Block部分三
Block知识点总结

以下内容的测试主要针对ARC环境; MRC下直接贴出测试结果, 不再贴出测试代码, 具体请自行测试MRC环境;

1. 首先是Block的分类

  • NSStackBlock : 访问了auto变量 == 存放在栈区
  • NSMallocBlock : 对__NSStackBlock__进行copy操作 == 存放在堆区
  • NSGlobalBlock : 没有访问auto变量 == 存放在数据区域
    他么的继承关系如图;
总结:

没有访问auto变量,其class__NSGlobalBlock__;
MRC下访问了auto变量, 其class就是__NSStackBlock__然后copy这个block;,其class变为__NSMallocBlock__; 没有访问auto变量,其class__NSGlobalBlock__;
ARC下访问了auto变量, 其class结果是__NSMallocBlock__, 因为系统自动帮忙进行了类似copy的操作将其赋值到堆区;


2. ARC下什么时候会对NSStackBlock进行copy操作?

  • 访问auto变量时;
  • 系统方法中有UsingBlockblock时;
  • 使用GCDblock时;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    ///最简单的block, 没有访问auto变量, 始终都是__NSGlobalBlock__
    NSLog(@"type: %@", [^{} class]);
    ///访问auto局部变量的block;  MRC是__NSStackBlock; ARC下系统copy操作到堆区, 所以就是__NSMallocBlock;
    int a = 10;
    void(^block1)(void) = ^{
        NSLog(@"%d", a);
    };
    NSLog(@"type: %@", [block1 class] );
  
    ///返回值block中没有访问auto局部变量; 始终都是__NSGlobalBlock__
    ReturnBlock block2 = [self getblock1];
    NSLog(@"block作为返回值时, type%@ ", [block2 class]);
  
    ///返回值block中有访问auto局部变量;MRC是__NSStackBlock; ARC下系统copy操作到堆区, 所以就是__NSMallocBlock;
    ReturnBlock block3 = [self getblock2];
    NSLog(@"block作为返回值时, type%@ ", [block3 class]);
    
    ///block被强指针指向, 有访问auto变量, ARC下会默认copy到堆区
    int b = 10;
    ReturnBlock block4 = ^{
        NSLog(@"%d",  b);
    };
    NSLog(@"block被强指针指向 , type%@ ", [block4 class]);

    ///系统方法中有usesblock的字眼的, ARC下会默认copy到堆区
    NSArray *arr = [NSArray array];
    [arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
    }];
    ///使用GCD中, ARC下会默认copy到堆区

}
- (ReturnBlock)getblock1 {
    ReturnBlock block = ^{
        NSLog(@"This is a block");
    };
    return  block;
}
- (ReturnBlock)getblock2 {
    int a = 10;
    ReturnBlock block = ^{
        NSLog(@"This is a block, a = %d", a);
    };
    return  block;
}
@end

3. Block中对象类型的强引用和弱引用?

情形1一个block对实例对象person1强引用:

代码如下;

- (void)test0 {
    TestBlock testblock;
    {
    Person *person1 = [[Person alloc] init];
    testblock =  ^{
            NSLog(@"person1 : %@", person1);
        };
    }
    testblock();
    ///block对person1强引用, 所以即使是出了{}作用域, 仍然可以打印出i其地址, 然后才会dealloc;
}

转换为C++代码后的block实现如下

struct __ViewController2__test0_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController2__test0_block_desc_0* Desc;
  ///对person对象进行了强引用;
  Person *__strong person1;
  __ViewController2__test0_block_impl_0(void *fp, struct __ViewController2__test0_block_desc_0 *desc, Person *__strong _person1, int flags=0) : person1(_person1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
情形2一个block对实例对象person2弱引用:

代码如下:

- (void)test1 {
    TestBlock testblock;
    {
        Person *person2 = [[Person alloc] init];
        __weak Person *weakPerson2 = person2;
        testblock =  ^{
            NSLog(@"person2 : %@", weakPerson2);
        };
    }
    testblock();
    ///block对weakPerson2弱引用,出了{}作用域后person2即销毁, 直接打印dealloc, 然后调用block时打印出的地址为null;
}

转换为C++代码后的block实现如下

struct __ViewController2__test1_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController2__test1_block_desc_0* Desc;
///对weakPerson2进行了弱引用
  Person *__weak weakPerson2;
  __ViewController2__test1_block_impl_0(void *fp, struct __ViewController2__test1_block_desc_0 *desc, Person *__weak _weakPerson2, int flags=0) : weakPerson2(_weakPerson2) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

通过指令将OC文件转换为C++文件
指令: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件.m -o 文件-arm64.cpp


参考文章和下载链接
测试代码
iOS clang指令报错问题总结
Apple 一些源码的下载地址
auto修饰符
C++中结构体的构造函数
全局变量、静态全局变量、静态局部变量和普通局部变量的区别

你可能感兴趣的:(iOS Block 部分二)