Block详解二(底层分析)
本人已迁移博客至掘进,以后会在掘进平台更新最新的文章也会有更多的干货,欢迎大家关注!!!https://juejin.im/user/588993965333309
Block专辑:
今天讲述Block的最后一篇,后两篇仅仅是加深1,2篇的理解,废话少说,开始讲解!
- __block细节
- __block内存管理
- 循环引用问题
一:__block细节
大家可能会遇到下面的问题,block的内部想要修改外部的auto变量,但是编译器会报问题!如下
如果block内部想要修改外部的auto变量,可以在int age 前面加入static修饰词,变为静态局部变量(会一直存在内存中,反而不好),以及可以将int age代码移植到函数外面变为全局变量! 除此之外还有没有其他的做法了呢,显然是有的,通过__block修饰,如下:
发现__block修改外面变量是可以达到目的的! 小结论
- __block可以用于解决block内部无法修饰auto变量值的问题
- __block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
通过命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
生成main.cpp代码查看原理
上面提到可以将int main函数的代码简化一下,简化成如下
首先拿到forwarding指针然后再拿到age的值
二:__block的内存管理
1. 当block在栈上时,并不会对__block变量产生强引用;
2. 当block被copy到堆时
- 会调用block内部的copy函数
- copy函数内部调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量形成强引用(retain)
3.当block从堆中移除时
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的__block变量(release)
拓展:__block的_forwarding指针
栈上block的forwarding指针指向堆上的block,而堆上block的forwarding指针指向自己本身的指针。
三:循环引用
关于block循环引用的基本概念,专辑block已经讲解,本篇讲述核心内容
1. 解决循环引用问题-ARC
(1) 用__weak、__unsafe_unretained解决
下面用例子来巩固下
int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [[Person alloc]init]; __weak Person *weakPerson = person; person.block = ^{ NSLog(@"age is %d", weakPerson.age); }; } NSLog(@"1111111"); return 0; } #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN
typedef void(^ZXYBlock)(void);
@interface Person : NSObject
@property(nonatomic,copy)ZXYBlock block; @property(assign, nonatomic)int age; @end #import "Person.h" @implementation Person -(void)dealloc { NSLog(@"%s", __func__); } @end
__weak方式解决循环引用问题
__unsafe_unretained方式解决循环引用问题
__weak,__unsafe_unretained都可以解决循环引用,有什么区别呢?
结论
相同点:__weak和__unsafe_unretained都不会产生强引用
不同点:__weak指向对象销毁时,会自动让指针置为nil;__unsafe_unretained不安全,指向对象销毁时,指针存储地址 不变,如果再次访问可能会造成野指针
(2) 用__block方式解决
下面来探究一下为什么__block可以解决循环引用?看下编译成的c++代码
以前结合__block 对象变量以及__block自动变量可知:c++包含了三个对象,如下
上面的三种关系如下,调用person = nil就是为了打断其中一个循环引用链条,但是必须要调用block()
2. 解决循环引用问题-MRC
MRC下,首先要在编译器上设置为MRC环境。Build Settings->Automatic Reference Counting设为No
(1) 用__unsafe_unretained解决
因为MRC下不存在弱指针,所以不存在__weak修饰解决循环引用
(2) 用__block解决
总结
上面是block最后一篇的讲解,关于block总共有四篇博客,应该可以讲解完所有关于block的内容,应该会大大增加大家对block底层的理解,如果觉得有意义有所帮助,欢迎点赞和关注,本人会及时更新博客!!!