这是我参与「第四届青训营 」笔记创作活动的第6天
引用:闭包与oc block(一) | 青训营笔记
block内存管理
基础分类
| NSGlobalBlock | NSStackBlock | NSMallocBlock |
|---|---|---|
| 全局Block,保存在数据区 | 栈Block | 堆Block |
| 定义在全局区,没有访问局部变量 | 访问了自动局部变量 | 对NSStackBlock调用了copy |
❓网上除了这几类,还有以下几类,看起来太像了但没找到它们有什么区别:
在 block runtime 中,第一了6种类型:
_NSConcreteStackBlock 栈上创建的block
_NSConcreteMallocBlock 堆上创建的block
_NSConcreteGlobalBlock 作为全局变量的block
_NSConcreteWeakBlockVariable
_NSConcreteAutoBlock
_NSConcreteFinalizingBlock
资料手册上写的很详细,自己个儿试一试
引用:三类Block
如果block被释放,有潜在的异常风险时,手动”帮你“copy下
ARC机制下自动调用copy的情况:
- block作为函数返回值:函数返回值存在堆区的情况下,函数作用域过了,函数返回值就释放了,ARC觉得不安全,于是自动copy到堆上,保证这个block脱离函数作用域后还能使用
- 将block赋值给strong指针:系统觉得给strong指针赋值block的时候,会希望block一直被持有,所以就把它copy到堆区进行一个内存的管理
- block作为某些系统方法参数:某些系统方法参数需要block保持一定生命周期内可用,会帮你调用copy方法,防止在不合时宜的时候被释放,避免发生意外
如图,第一种是原来的写法,依旧输出
__NSMallocBlock__,声明的时候,void (^stackBlock0) (void)没有写修饰符,默认为strong。给strong指针赋值一个block,系统会自动copy成一个堆区的block。
第二种先声明block,且没有直接给block赋值,而是直接打印这个block,输出的就是__NSStackBlock__的block(显示"unused variable 'stockBlock1'"可能就是因为没有赋值而是直接打印)
变量捕获
情况一:值引用
输出是1,2
第一个打印是block内部的打印,第二个是外部的打印。因为oneBlock在初始化时捕获了value,此时value为1;后来当value更改后,block内捕获的值并没有跟着一起变化。
❗️另外,调用定义的方法公式要记住了😭查了好久
情况二:名称引用
加入__block后,允许被修饰变量在block内被修改,block内部对于value的捕获为引用捕获,内外相互影响。
下列为两个变式:
输出是2,2
输出是2,3
在block代码块内修改捕获变量的值:
- 全局变量
- 用static修饰的局部变量
- 使用关键字
__block
__block不能修饰全局变量、静态变量static
引用:block捕获变量与循环引用问题
除了int类型,再尝试一下其他类型:
NSString
block捕获对象属性
这里仍然是值引用,但输出为2,2,原因如下:
block捕获的是self对象(给self发消息,取name),self对象没变,但self对象的name属性改变。
类似名称引用,但本质是值引用
oc方法运行都是发消息,将oc转化为c++会更清晰
循环引用(内存泄漏)
warning: Capturing 'self' strongly in this block is likely to lead to a retain cycle!
原因:block捕获了self,又被self持有,block和self之间有循环引用的关系
❗️❗️但是但是[super viewDidLoad];这句话的bug我是真不会改啊,改太久了也查不到气死人了,这个之后发现错哪了再来更新,有缘再见吧。
修改报错:
-
No visible @interface for 'NSObject' declares the selector 'viewDidLoad'
原因:在 ViewController.h 文件中 写的 ViewController : NSObject 而不是 ViewController : UIViewController, NSObject 中没有 viewDidLoad 方法,是UIViewController 类中的 -
Cannot find interface declaration for 'UIViewController', superclass of 'ViewController'
原因:有UIKit框架就有UIViewcontroller,加上
#import <UIKit/UIKit.h> -
'UIKit/UIKit.h' file not found
原因:macOS只能使用
AppKit作为UI框架引用:找不到MacOS-UIKit/UIKit.h
-
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应用