闭包与oc block(二) | 青训营笔记

98 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

引用:闭包与oc block(一) | 青训营笔记

网址:juejin.cn/post/712710…

block内存管理

基础分类

NSGlobalBlockNSStackBlockNSMallocBlock
全局Block,保存在数据区栈Block堆Block
定义在全局区,没有访问局部变量访问了自动局部变量对NSStackBlock调用了copy

❓网上除了这几类,还有以下几类,看起来太像了但没找到它们有什么区别:

在 block runtime 中,第一了6种类型:
_NSConcreteStackBlock 栈上创建的block
_NSConcreteMallocBlock 堆上创建的block
_NSConcreteGlobalBlock 作为全局变量的block
_NSConcreteWeakBlockVariable
_NSConcreteAutoBlock
_NSConcreteFinalizingBlock

资料手册上写的很详细,自己个儿试一试

引用:三类Block

网址:juejin.cn/post/712344…

三类Block代码尝试.png 原因.png

如果block被释放,有潜在的异常风险时,手动”帮你“copy下

ARC机制下自动调用copy的情况:

  1. block作为函数返回值:函数返回值存在堆区的情况下,函数作用域过了,函数返回值就释放了,ARC觉得不安全,于是自动copy到堆上,保证这个block脱离函数作用域后还能使用
  2. 将block赋值给strong指针:系统觉得给strong指针赋值block的时候,会希望block一直被持有,所以就把它copy到堆区进行一个内存的管理
  3. block作为某些系统方法参数:某些系统方法参数需要block保持一定生命周期内可用,会帮你调用copy方法,防止在不合时宜的时候被释放,避免发生意外

如何不被copy.png 如图,第一种是原来的写法,依旧输出__NSMallocBlock__,声明的时候,void (^stackBlock0) (void)没有写修饰符,默认为strong。给strong指针赋值一个block,系统会自动copy成一个堆区的block。
第二种先声明block,且没有直接给block赋值,而是直接打印这个block,输出的就是__NSStackBlock__的block(显示"unused variable 'stockBlock1'"可能就是因为没有赋值而是直接打印)

变量捕获

情况一:值引用 changeValue.m.png main.m.png 输出是1,2
第一个打印是block内部的打印,第二个是外部的打印。因为oneBlock在初始化时捕获了value,此时value为1;后来当value更改后,block内捕获的值并没有跟着一起变化
❗️另外,调用定义的方法公式要记住了😭查了好久

情况二:名称引用
加入__block后,允许被修饰变量在block内被修改,block内部对于value的捕获为引用捕获内外相互影响
下列为两个变式: 加入__block变式1.png 输出是2,2

加入__block变式2.png 输出是2,3

在block代码块内修改捕获变量的值:

  1. 全局变量
  2. 用static修饰的局部变量
  3. 使用关键字__block

__block不能修饰全局变量、静态变量static

引用:block捕获变量与循环引用问题

网址:www.jianshu.com/p/18bc79eda…

除了int类型,再尝试一下其他类型: NSString NSString1.png NSString2.png

block捕获对象属性 property.png property.png 这里仍然是值引用,但输出为2,2,原因如下:
block捕获的是self对象(给self发消息,取name),self对象没变,但self对象的name属性改变。
类似名称引用,但本质是值引用

oc方法运行都是发消息,将oc转化为c++会更清晰

捕获总结.png

循环引用(内存泄漏)

block循环引用.png warning: Capturing 'self' strongly in this block is likely to lead to a retain cycle!
原因:block捕获了self,又被self持有,block和self之间有循环引用的关系

❗️❗️但是但是[super viewDidLoad];这句话的bug我是真不会改啊,改太久了也查不到气死人了,这个之后发现错哪了再来更新,有缘再见吧。

修改报错:

  1. No visible @interface for 'NSObject' declares the selector 'viewDidLoad'
    原因:在 ViewController.h 文件中 写的 ViewController : NSObject 而不是 ViewController : UIViewController, NSObject 中没有 viewDidLoad 方法,是UIViewController 类中的

  2. Cannot find interface declaration for 'UIViewController', superclass of 'ViewController' 找不到UIViewController.png 原因:有UIKit框架就有UIViewcontroller,加上#import <UIKit/UIKit.h>

  3. 'UIKit/UIKit.h' file not found 找不到UIKit/UIKit.h.png 原因:macOS只能使用AppKit作为UI框架

    引用:找不到MacOS-UIKit/UIKit.h

    网址:www.5axxw.com/questions/c…

  4. Cannot find interface declaration for 'UIViewController', superclass of 'ViewController'; did you mean 'NSViewController'?
    原因:应该是用了AppKit作为UI框架的原因

打破引用环:其中一个采用弱引用weak
block弱引用self,在外面将self weak,得到weakSelf,在block内手动strong一下,使用strongSelf

为什么不直接应用weakSelf:
weakSelf是个弱指针,并不持有self,可能出现block执行时,self释放变成new,出现部分代码未执行完的情况;
在strongSelf的情况下,可以保证block执行过程中,strongSelf全程都在,block运行完后出了作用域,strongSelf释放,也不会过多的持有self

- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.completionBlock = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        NSLog(@"%@", strongSelf.name);
    };
}

因为[super viewDidLoad];一直报错,就不写在xcode然后截图了

隐式内存泄漏

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.completionBlock = ^{
        NSLog(@"%@", _name);
    };
}

这个_name 本质上还是self.name

block的应用

block应用

网址:juejin.cn/post/712344…