请选择 进入手机版 | 继续访问电脑版

[IOS] iOS block循环引用详解及常见误区

[复制链接]
查看139 | 回复26 | 2021-9-13 16:05:22 | 显示全部楼层 |阅读模式

Block循环引用

什么环境 下block会造成循环引用

ARC 环境 下 block为了保证代码块内部对象不被提前开释 ,会对block中的对象举行 强引用,就相当 于持有了此中 的对象,而假如 此时block中的对象又持有了该block,就会造成循环引用。

常见误区

误区一.全部 block都会造成循环引用

在block中,并不是全部 的block都会循造成环引用,比如UIView动画block、Masonry添加束缚 block、AFN网络哀求 回调block等。    

1. UIView动画block不会造成循环引用是由于 这是类方法,不大概 强引用一个类,以是 不会造成循环引用。    

2. Masonry束缚 block不会造成循环引用是由于 self并没有持有block,以是 我们使用 Masonry的时间 不必要 担心循环引用。

Masonry内部代码

  1. - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
  2. self.translatesAutoresizingMaskIntoConstraints = NO;
  3. MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
  4. //这里并不是self.block (self并没有持有block 所以不会引起循环引用)
  5. block(constraintMaker);
  6. return [constraintMaker install];
  7. }
复制代码

3.AFN哀求 回调block不会造成循环引用是由于 在内部做了处理。
block先是被

  1. AFURLSessionManagerTaskDelegate
复制代码
对象持有。而
  1. AFURLSessionManagerTaskDelegate
复制代码
对象被
  1. mutableTaskDelegatesKeyedByTaskIdentifier
复制代码
字典持有,在block实验 完成后,
  1. mutableTaskDelegatesKeyedByTaskIdentifier
复制代码
字典会移除
  1. AFURLSessionManagerTaskDelegate
复制代码
对象,如许 对象就被开释 了,以是 不会造成循环引用。

AFN内部代码

  1. #pragma mark - 添加代理
  2. - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
  3. uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
  4. downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
  5. completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
  6. {
  7. AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
  8. delegate.manager = self;
  9. //block被代理引用
  10. delegate.completionHandler = completionHandler;
  11. dataTask.taskDescription = self.taskDescriptionForSessionTasks;
  12. //设置代理
  13. [self setDelegate:delegate forTask:dataTask];
  14. delegate.uploadProgressBlock = uploadProgressBlock;
  15. delegate.downloadProgressBlock = downloadProgressBlock;
  16. }
  17. #pragma mark - 设置代理
  18. - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
  19. forTask:(NSURLSessionTask *)task
  20. {
  21. NSParameterAssert(task);
  22. NSParameterAssert(delegate);
  23. [self.lock lock];
  24. //代理被mutableTaskDelegatesKeyedByTaskIdentifier字典引用
  25. self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
  26. [delegate setupProgressForTask:task];
  27. [self addNotificationObserverForTask:task];
  28. [self.lock unlock];
  29. }
  30. #pragma mark - 任务完成
  31. - (void)URLSession:(NSURLSession *)session
  32. dataTask:(NSURLSessionDataTask *)dataTask
  33. didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
  34. {
  35. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
  36. if (delegate) {
  37. //任务完成,移除
  38. [self removeDelegateForTask:dataTask];
  39. [self setDelegate:delegate forTask:downloadTask];
  40. }
  41. if (self.dataTaskDidBecomeDownloadTask) {
  42. self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
  43. }
  44. }
  45. #pragma mark - 移除任务代理
  46. - (void)removeDelegateForTask:(NSURLSessionTask *)task {
  47. NSParameterAssert(task);
  48. AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
  49. [self.lock lock];
  50. [delegate cleanUpProgressForTask:task];
  51. [self removeNotificationObserverForTask:task];
  52. //移除
  53. [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
  54. [self.lock unlock];
  55. }
复制代码

误区二.block中只有self会造成循环引用

在block中并不只是self会造成循环引用,用下划线调用属性(如_name)也会出现循环引用,结果 和使用 self是一样的(内部会用self->name去查找)。

  1. //会造成循环引用
  2. _person1 = [[Person alloc] init];
  3. _person2 = [[Person alloc] init];
  4. _person2.name = @"张三";
  5. [_person1 Block:^{
  6. NSLog(@"%@",_person2.name)
  7. }];
复制代码

误区三.通过__weak __typeof(self) weakSelf = self;可以办理 全部 block造成的循环引用

大部分环境 下,如许 使用 是可以办理 block循环引用,但是有些环境 下如许 使用 会造成一些题目 ,比如在block中耽误 实验 一些代码,在还没有实验 的时间 ,控制器被烧毁 了,如许 控制器中的对象也会被开释 ,__weak对象就会变成null。以是 会输出null。

  1. //在延迟执行期间,控制器被释放了,打印出来的会是**(null)**
  2. _person1 = [[Person alloc] init];
  3. _person2 = [[Person alloc] init];
  4. _person2.name = @"张三";
  5. __weak __typeof(self) weakSelf = self;
  6. [_person1 Block:^{
  7. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  8. NSLog(@"%@",weakSelf.person2.name);
  9. });
  10. }];
复制代码

误区四.用self调用带有block的方法会引起循环引用

并不是全部 通过self调用带有block的方法会引起循环引用,必要 看方法内部有没有持有self。

  1. //不会引起循环引用
  2. [self dismissViewControllerAnimated:YES completion:^{
  3. NSLog(@"%@",self.string);
  4. }];
复制代码

怎样 避免循环引用

方式一、weakSelf、strongSelf连合 使用

使用

  1. weakSelf
复制代码
连合
  1. strongSelf
复制代码
的环境 下,可以或许 避免循环引用,也不会造成提前开释 导致block内部代码无效。

  1. _person1 = [[Person alloc] init];
  2. _person2 = [[Person alloc] init];
  3. _person2.name = @"张三";
  4. __weak __typeof(self) weakSelf = self;
  5. [_person1 Block:^{
  6. __typeof(&*weakSelf) strongSelf = weakSelf;
  7. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  8. NSLog(@"%@",strongSelf.person2.name);
  9. });
  10. }];
复制代码

方式二、block的外部对象使用 week

外部对象通过week修饰,使用 全局弱指针指向一个局部强引用对象,如许 局部变量在超出其作用域后也不会被烧毁 ,由于 是弱指针,以是 不会造成循环引用。

  1. @interface CLViewController ()
  2. //弱引用指针
  3. @property (nonatomic,weak) Person *person1;
  4. @property (nonatomic,strong) Person *person2;
  5. @end
  6. @implementation CLViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. self.view.backgroundColor = [UIColor redColor];
  10. //局部强引用对象
  11. Person *person1 = [[Person alloc] init];
  12. _person1 = person1;
  13. _person2 = [[Person alloc] init];
  14. _person2.name = @"张三";
  15. [_person1 Block:^{
  16. NSLog(@"%@",self.person2.name);
  17. }];
  18. }
复制代码

方式三.将对象置为nil

使用 完对象之后就没有必要再保留该对象了,将对象置为nil。在ARC中,被置为nil的对象会被烧毁 。固然 如许 也可以达到清除 循环引用的结果 ,但是如许 使用 起来很不方便,假如 一个对象有多个block,在前面将对象置空,后面的block就不会实验 ,以是 使用 这种方法来防止循环引用,必要 注意 在合适的位置将对象置空。

  1. _person1 = [[Person alloc] init];
  2. _person2 = [[Person alloc] init];
  3. _person2.name = @"张三";
  4. [_person1 Block:^{
  5. NSLog(@"%@",self.person2.name);
  6. //置空,避免循环引用
  7. _person1 = nil;
  8. }];
  9. //由于上面已经将对象置空,所以这里block里边的代码不会执行
  10. [_person1 Block:^{
  11. NSLog(@"%@",self.person2.name);
  12. }];
复制代码

固然 这种方式使用 起来不是很友好 ,但是在封装block的时间 ,可以思量 使用 完立刻 置空当前使用 的block,如许 使用 的时间 就不必要 思量 循环引用的题目 。

  1. - (void)back
  2. {
  3. if (self.BackBlock)
  4. {
  5. self.BackBlock(button);
  6. }
  7. //使用完,马上置空当前block
  8. self.BackBlock = nil;
  9. }
复制代码

总结

使用 block的时间 ,我们起首 必要 做的就是判断 当前block是否会引起循环引用,假如 会引起循环引用,再思量 采取哪种方式来避免循环引用。

到此这篇关于iOS block循环引用详解和应用的文章就先容 到这了,更多干系 iOS block循环引用内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

avatar 天然悠然牌 | 2021-9-18 11:24:21 | 显示全部楼层
好东西,学习学习!
回复

使用道具 举报

avatar 司驴迁咏 | 2021-9-19 10:25:29 | 显示全部楼层
十分赞同admin楼主!
回复

使用道具 举报

avatar 准藏智虽 | 2021-9-20 14:29:31 | 显示全部楼层
青春不在了,青春痘还在!
回复

使用道具 举报

avatar Alysia | 2021-10-4 09:57:36 | 显示全部楼层
收藏了,怕admin楼主删了!
回复

使用道具 举报

avatar 随缘6872 | 2021-10-18 20:28:17 | 显示全部楼层
灌水不是我的目的!
回复

使用道具 举报

支持楼上的!
回复

使用道具 举报

很多天不上线,一上线就看到这么给力的帖子!
回复

使用道具 举报

admin楼主英明!
回复

使用道具 举报

在这个版块混了这么久了,第一次看见这么给你的帖子!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则