前言
1.block上层面试
2.实际业务开发 - 问题,循环引用
3.block底层分析-结构体,block_invoke就是回调,可进行hook
种类
- 全局BLock,NSGlobalBlock,位于全局区,在Block内部不使用外部变量,或者只使用静态变量和全局变量。
- 堆Block,NSMallocBlock,位于堆区,在Block内使用变量或者OC属性,并赋值给强引用或者Copy修饰的变量。
- 栈Block,NSStackBlock,位于栈区,与堆Block一样,在Block内使用变量或者OC属性,但不能赋值给强引用或者Copy修饰的变量。
举例
demo1
- 等号前的block相当于指向了等号后的block的内存空间
- 类似 NSObject *obj = [NSObject alloc] 中的obj指向了=后的内存空间
demo2
- 以下打印结果为1 —> 3 —> 4 —> 5
- block捕获对象时,引用计数+1;从栈区block拷贝到堆区时,block会再次+1
- 首先栈内生成一个变量,对捕获而来的对象存储,然后block_ref从栈区拷贝到堆区,相当于又复制了一份
- 第二步骤 = 第三步骤 + 第四步骤
demo3
- 内存拷贝的理解
- 建立一个栈区block,同时自定义个block(结构体),此时两个block指向同一内存空间,再将block的invoke置为nil,执行strongBlock1()则发生崩溃
- 将赋值改为copy则可以避免崩溃id__strong strongBlock = [weakBlock copy],此时栈block已经被拷贝到堆区,再赋值给_LGBlock,执行invoke = nil,不影响拷贝后的strongBlock1()
demo4
- 此处代码执行会崩溃,原因是堆block超出作用域被释放了
- strongBlock 加__weak 时,为栈block,压栈进栈帧,作用域为整个函数,所以不会崩溃
循环引用
- 相互持有导致retainCount无法减少,无法释放
- 一般我们通过weakSelf,strongSelf来解决循环引用,但也有其它方式,如下例
- 强弱共舞
- __block ViewController *vc = self
- vc持有了self ,作用域在^{}中,使用完要置为nil,否则会出现self -> block -> vc -> self,循环引用
- vc作用域初始在栈帧中,作用域是外部{},后被block持有了,引用计数又增加了1
- 或者将self当做参数传递,都可以解决循环引用问题
demo5
- staticSelf全局静态变量,将weakSelf赋值给staticSelf,是否会引起循环引用?
- weakSelf和self是映射关系,指向同一片内存空间
- 将weakSelf赋值给全局静态变量,相当于staticSelf对self进行了持有,导致了循环引用 ```objectivec
(void)blockWeakstatic { // 是同一片内存空间 __weak typeof(self) weakSelf = self; staticSelf = weakSelf; // staticSelf_ -> weakSelf -> self } ```
weakSelf.doStudent又对strongSelf进行了持有,引用计数进行了+1,导致循环引用 ```objectivec
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
}; self.doWork(); } ```__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();