Block内存管理

186 阅读1分钟

我们先看一下iOS的内存分布,从高地址->低地址依次为

1:栈
2:堆
3:全局区(全局静态区-常量区-代码区)
一般情况栈区0x7开头,堆区0x6,  全局区0x1

摘录一张图片 www1.png

Block的类型

全局 NSGlobalBlock

在block内部不使用外部变量,或者只使用静态变量和全局变量 截屏2022-01-29 11.02.19.png

NSMallocBlock

在block 内部使用变量或者OC属性,并且赋值给强引用或者Copy修饰的变量 截屏2022-01-29 10.54.42.png

NSStackBlock

和上面的NSMallocBlock一样,可以在内部使用局部变量或者OC属性,但是不能赋值给强引用或者copy修饰的变量 截屏2022-01-29 10.54.09.png

我们看如下面试题 Xnip2022-01-29_11-19-12.jpg 我们会疑惑第二个输出语句,为什么输出 3

  • 第一点是因为,block的捕获, block捕获了objc 使得引用计数 + 1
  • 第二点是因为堆block的底层实现,会将block内容整体copy一份 如果将如上代码改成如下 Xnip2022-01-29_11-29-14.jpg 我们看到这时block为__NSStackBlock__,栈block不会进行copy,所以输出2

Block循环引用

self.myBlcok = ^{
   NSLog(@"%@",self.name);
};
self.myBlcok();

如上代码会产生循环引用,我们采用常用的__weak方式去解决

__weak typeof(self) weakSelf = self;
self.myBlcok = ^{
    NSLog(@"%@",weakSelf.name);
};
self.myBlcok();

使用__weak 是可以解决循环引用的问题,但是有个问题,当我们在block内部使用了多线性, 或者延时的时候,可能产生问题。如下

__weak typeof(self) weakSelf = self;
self.myBlcok = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                     (int64_t)(5 *NSEC_PER_SEC)),
                                     dispatch_get_main_queue(), ^{
          NSLog(@"name:%@",weakSelf.name);
      });
};
self.myBlcok();

当我们进入页面,立刻退出页面的时候,过一会发现打印的name:(null) 是由于页面已经释放,所以出现了如上情况,为了解决这种问题,我们采用更严谨的处理方式

__weak typeof(self) weakSelf = self;
self.myBlcok = ^{
     __strong typeof(weakSelf) strongSelf = weakSelf;
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                     (int64_t)(5 *NSEC_PER_SEC)),
                                     dispatch_get_main_queue(), ^{
            NSLog(@"name:%@",strongSelf.name);
      });
};
self.myBlcok();