【iOS】——Block循环引用
创始人
2024-12-03 21:04:15
0

循环引用原因

如果在Block中使用附有_ _strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用。

HPPerson *person = [[HPPerson alloc] init];  person.block = ^{              NSLog(@"person.age--- %d",person.age);  };  

在上面代码中,person对象强持有block对象,在block语法中,block对象又强持有person对象,此时达成互相强持有,谁也无法释法谁,造成循环引用。

在这里插入图片描述

当造成block循环引用时编译器会检测出并发出警告

在这里插入图片描述

另外,如果block内没有使用self也会捕获self,引起循环引用

typedef void (^blk_t) (void);  @interface HPPerson : NSObject {     blk_t _block;     int _age; } @end #import "HPPerson.h"  @implementation HPPerson -(id)init {     self = [super init];     _block = ^{NSLog(@"age = %d", _age);};     return self; }  - (void)dealloc {     NSLog(@"%s", __func__); } @end 

在这里插入图片描述

这是因为虽然没有使用self,但使用了self对象中的结构体成员,因此也会捕获self。

避免循环引用

使用weak修饰符

int main(int argc, const char * argv[]) {     @autoreleasepool {         HPPerson *person = [[HPPerson alloc] init];         person.age = 10;                  __weak HPPerson *weakPerson = person;                  person.block = ^{             NSLog(@"person.age--- %d",weakPerson.age);         };         NSLog(@"--------");      }     return 0; }  

编译完成之后是

struct __main_block_impl_0 {   struct __block_impl impl;   struct __main_block_desc_0* Desc;     // block内部对weakPerson是弱引用   HPPerson *__weak weakPerson;   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {     impl.isa = &_NSConcreteStackBlock;     impl.Flags = flags;     impl.FuncPtr = fp;     Desc = desc;   } };  

局部变量消失时候,对于HPPerson来说,只有一个弱指针指向它,那它就销毁,然后block也销毁。在这里插入图片描述

使用__unsafe_unretained修饰符

int main(int argc, const char * argv[]) {     @autoreleasepool {         HPPerson *person = [[HPPerson alloc] init];         person.age = 10;                  __unsafe_unretained HPPerson *weakPerson = person;                  person.block = ^{             NSLog(@"person.age--- %d",weakPerson.age);         };         NSLog(@"--------");      }     return 0; }  

编译完成之后是

struct __main_block_impl_0 {   struct __block_impl impl;   struct __main_block_desc_0* Desc;   HPPerson *__unsafe_unretained weakPerson;   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {     impl.isa = &_NSConcreteStackBlock;     impl.Flags = flags;     impl.FuncPtr = fp;     Desc = desc;   } };  

虽然__unsafe_unretained可以解决循环引用,但是最好不要用,因为:

  • __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
  • __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变,会造成野指针

使用_ _Block修饰符

int main(int argc, const char * argv[]) {     @autoreleasepool {        __block HPPerson *person = [[HPPerson alloc] init];         person.age = 10;         person.block = ^{             NSLog(@"person.age--- %d",person.age);             //这一句不能少             person = nil;         };         // 必须调用一次         person.block();         NSLog(@"--------");     }     return 0; }  

使用_ _Block修饰符解决循环引用时,需要注意的点有:

  • 在block对象中需要将_ _block变量置为nil
  • 必须调用block对象

如果不调用block对象时,会造成下面情况的循环引用:

  1. HPPerosn类对象持有Block
  2. Block持有_ _block变量
  3. _ _block变量持有HPPerson对象

在这里插入图片描述

因为block会对__block产生强引用

__block HPPerson *person = [[HPPerson alloc] init]; person.block = ^{         NSLog(@"person.age--- %d",person.age);         //这一句不能少         person = nil; };  

person对象本身就对block是强引用

@property (copy, nonatomic) HPBlock block;  

__block对person产生强引用

struct __Block_byref_person_0 {   void *__isa; __Block_byref_person_0 *__forwarding;  int __flags;  int __size;  void (*__Block_byref_id_object_copy)(void*, void*);  void (*__Block_byref_id_object_dispose)(void*);     //`__block`对person产生强引用  HPPerson *__strong person; };  

当执行完person = nil时候,__block解除对person的引用,进而,全都解除释放了。 但是必须调用person = nil才可以,否则,不能解除循环引用

在这里插入图片描述

强弱共舞

  • 当Block捕获self时,应该使用弱引用,这样即使Block持有self的引用,也不会阻止self被释放。
  • 由于弱引用可能变成nil,因此在Block内部使用self之前,需要检查它是否为nil
  • 为了避免在Block内部因selfnil而导致的崩溃,可以在Block的开始处使用强引用
  • 使用完成之后当Block的作用域结束之后即可释放
#import  typedef void(^blk_t)(void); @interface ViewController : UIViewController @property (nonatomic, strong) blk_t block; @property (nonatomic, copy) NSString *name;  @end #import "ViewController.h"  @interface ViewController ()  @end  @implementation ViewController  - (void)viewDidLoad {     [super viewDidLoad];     // Do any additional setup after loading the view.     self.name = @"Hello";     __weak typeof(self) weakSelf = self;     self.block = ^(){          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             NSLog(@"%@", strongWeak.name);      };      self.block(); }   @end 

此时self持有block,block弱引用self,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。但该方法可能存在中途就释放掉的问题(手动延迟,可能需要调用self.name的时候name已经被释放了)如果self被销毁,那么block则无法获取name。

因此可以改进上面的代码:

    self.name = @"Hello";      __weak typeof(self) weakSelf = self;     self.block = ^(){         __strong __typeof(weakSelf)strongWeak = weakSelf;          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             NSLog(@"%@", strongWeak.name);         });     };     self.block();  

在完成block中的操作之后,才调用了dealloc方法。添加strongWeak之后,持有关系为:self -> block -> strongWeak -> weakSelf -> self。

weakSelf被强引用了就不会自动释放,因为strongWeak只是一个临时变量,它的声明周期只在block内部,block执行完毕后,strongWeak就会释放,而弱引用weakSelf也会自动释放。

参数形式解决循环引用

通过给block传参(指针拷贝)

    // 循环引用     self.name = @"Hello";     self.block = ^(ViewController * ctrl){         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             NSLog(@"%@", ctrl.name);         });     };     self.block(self);  

Block循环引用场景

    // staticSelf_定义:     static ViewController *staticSelf_;      - (void)blockWeak_static {         __weak typeof(self) weakSelf = self;         staticSelf_ = weakSelf;     }  

weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用

相关内容

热门资讯

透视代打!wepokerplu... 透视代打!wepokerplus透视脚本免费,从来是有挂(透视)我来教教你(有挂黑科技)1、这是跨平...
透视app"werp... 透视app"werplan辅助软件"原来有挂(透视)必赢方法(有挂规律);所有人都在同一条线上,像星...
透视科技!aapoker怎么设... 透视科技!aapoker怎么设置提高好牌几率(透视)透视脚本入口(切实有挂);1、该软件可以轻松地帮...
透视能赢!线上德州的辅助器是什... 透视能赢!线上德州的辅助器是什么,一向是有挂(透视)wpk教程(有挂详情)1、线上德州的辅助器是什么...
透视系统"werpl... 透视系统"werplan辅助软件"一直真的有挂(透视)攻略教程(有挂攻略)1、werplan辅助软件...
透视黑科技!aapoker俱乐... 透视黑科技!aapoker俱乐部靠谱吗(透视)透视脚本(真是是有挂)1、aapoker俱乐部靠谱吗系...
透视好友房!德扑圈有透视吗,往... 透视好友房!德扑圈有透视吗,往昔是有挂(透视)微扑克教程(有挂工具);1、实时德扑圈有透视吗开挂更新...
透视挂"wepoke... 透视挂"wepoker免费透视脚本"都是真的是有挂(透视)介绍教程(有挂辅助)在进入wepoker免...
辅助透视!aapoker透视脚... 辅助透视!aapoker透视脚本(透视)免费透视脚本(一贯是有挂)1、该软件可以轻松地帮助玩家将aa...
透视黑科技!哈糖大菠萝有挂吗,... 透视黑科技!哈糖大菠萝有挂吗,总是是真的有挂(透视)解密教程(有挂技巧)1、哈糖大菠萝有挂吗ai辅助...