原因:

self -> Timer -> target(self), 造成循环引用

导致控制器不会销毁,不会调用dealloc 方法,内存泄漏

  1. - (void)dealloc{
  2. [_timer invalidate];
  3. NSLog(@"********************delloc**************8");
  4. }

解决方式:

1.API block的方式 iOS10以后可用

  1. __weak typeof(self)weakself = self;
  2. _timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
  3. weakself.count++;
  4. NSLog(@"-------%ld",weakself.count);
  5. }];

2.手动破解循环

在适当的时机调用 (达到一定条件或者 例如在- (void)viewDidDisappear:(BOOL)animated {}调用,)

  1. [self.timer invalidate];
  2. self.timer = nil;

3. 借助runtime给对象添加消息处理的能力, 这种方式虽然能破解循环,但是 test 方法里获取到的self 是 _objct ,故感觉不太实用

  1. // 借助runtime给对象添加消息处理的能力
  2. _objct = [[NSObject alloc] init];
  3. class_addMethod([_objct class], @selector(test), class_getMethodImplementation([self class], @selector(test)), "v@:");
  4. _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:@"ddd" repeats:YES];

4.消息转发机制

创建一个中间类 PHJProxy,

  1.  

@interface PHJProxy : NSObject

@property (nonatomic, weak) id target;

@end


  1. @implementation PHJProxy
  2.  
  3. //方法1:
  4. //// 发送给target
  5. //- (void)forwardInvocation:(NSInvocation *)invocation {
  6. // [invocation invokeWithTarget:self.target];
  7. //}
  8. //// 给target注册一个方法签名
  9. //- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
  10. // return [self.target methodSignatureForSelector:sel];
  11. //}
  12.  
  13. // 方法2:
  14. //仅仅添加了weak类型的属性还不够,为了保证中间件能够响应外部self的事件,需要通过消息转发机制,让实际的响应target还是外部self,这一步至关重要,主要涉及到runtime的消息机制。
  15. -(id)forwardingTargetForSelector:(SEL)aSelector {
  16. return self.target;
  17. }
  18.  
  19. @end

这2种方式原理都是一样,使用消息的转发机制,把消息转发到 target 中

补充:如果PHJProxy类 继承至NSProxy 时:

  1. @interface LWProxy : NSProxy
  2.  
  3. @property (nonatomic, weak) id target;
  4.  
  5. + (instancetype)proxyWithTarget:(id)target;
  6.  
  7. @end
  8.  
  9. /**
  10. NSProxy 类,是专门做消息转发的代理类,如果本类中没有方法实现,则不会去父类中查找,直接进入 -()methodSignatureForSelector{},进行转发
  11. 因此此类做消息转发比NSObject 效率更高
  12. */
  13. @implementation LWProxy
  14.  
  15. + (instancetype)proxyWithTarget:(id)target
  16. {
  17. // 只有alloc方法,没有init方法
  18. LWProxy *proxy = [LWProxy alloc];
  19. proxy.target = target;
  20. return proxy;
  21. }
  22.  
  23. - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
  24. {
  25. return [self.target methodSignatureForSelector:sel];
  26. }
  27.  
  28. - (void)forwardInvocation:(NSInvocation *)invocation
  29. {
  30. [invocation invokeWithTarget:self.target];
  31. }
  32.  
  33. @end

5. NSTimer 分类的方式,

  1. @implementation NSTimer (LWTimer)
  2. + (NSTimer *)lw_timerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats
  3. {
  4. return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(timermethod:) userInfo:[block copy] repeats:YES];
  5. }
  6.  
  7. + (void)timermethod:(NSTimer *)timer
  8. {
  9. void (^block)() = timer.userInfo;
  10. if (block) {
  11. block();
  12. }
  13. }
  14. @end

上述创建方式调用者是NSTImer自己,只是NSTimer捕获了参数block。这样我们在使用timer时,由于target的改变,就不再有循环引用了。

使用方式:

__weak typeof(self)weakself = self;

  1. _timer = [NSTimer lw_timerWithTimeInterval:2 block:^{
  2. weakself.count++;
  3. NSLog(@"-------%ld",weakself.count);
  4. } repeats:YES];

iOS10中,定时器的API新增了block方法,实现原理与此类似,这里采用分类为NSTimer添加了带有block参数的方法,而系统是在原始类中直接添加方法,最终的行为是一致的。

注意:

1、把timer改成弱引用

  1. @property (nonatomic, weak) NSTimer *timer;
虽然self对timer是弱引用,但是控制的delloc方法的执行依赖于timer的invalidate,timer的invalidate又依赖于控制器的delloc方法,这是一个鸡生蛋还是蛋生鸡的问题,依旧是循环引用

2. 使用__weak 那换个思路能不能让NSTimer弱引用target

  1. __weak typeof(self) weakSelf = self;
  2.  
  3. self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(showMsg) userInfo:nil repeats:YES];
weak关键字适用于block,当block引用了块外的变量时,会根据修饰变量的关键字来决定是强引用还是弱引用,如果变量使用weak关键字修饰,那block会对变量进行弱引用,如果没有__weak关键字,那就是强引用。
  但是NSTimer的 scheduledTimerWithTimeInterval:target方法内部不会判断修饰target的关键字,所以这里传self 和 weakSelf是没区别的,其内部会对target进行强引用,还是会产生循环引用。

iOS 循环引用的问题总结的更多相关文章

  1. iOS循环引用

    iOS循环引用 当前类的闭包/Block属性,用到了当前类,就会造成循环引用 此闭包/Block应该是当前类的属性,我们经常对Block进行copy,copy到堆中,以便后用. 单方向引用是不会产生循 ...

  2. iOS 循环引用解决方案

    一.BLOCK 循环引用 一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身.构成循环引用. // 定义 block 的时候,会对外部变量做一次 cop ...

  3. iOS 循环引用

    1.循环引用一般是指:A持有B,B同时持有A,从而导致死循环无法释放对象. 2.一般循环引用出现在block和delegate中,而一般解决方法就是将self变成weakSelf(强引用变成弱引用), ...

  4. iOS循环引用问题

    今天面试问道了循环引用,所以就看了看,原来只是知道使用了Block容易造成循环引用.今天就来简单的介绍一些循环引用. 先来简单介绍一下什么是循环引用? 循环引用可以简单的理解成:A对象引用了B对象,B ...

  5. iOS 循环引用讲解(中)

    谈到循环引用,可能是delegate为啥非得用weak修饰,可能是block为啥要被特殊对待,你也可能仅仅想到了一个weakSelf,因为它能解决99%的关于循环引用的事情.下面我以个人的理解谈谈循环 ...

  6. iOS循环引用常见场景和解决办法

    好多场景会导致循环引用,例如使用Block.线程.委托.通知.观察者都可能会导致循环引用. 1.委托 遵守一个规则,委托方持有代理方的强引用,代理方持有委托方的弱引用. 实际场景中,委托方会是一个控制 ...

  7. iOS 循环引用 委托 (实例说明)

    如何避免循环引用造成的内存泄漏呢: 以delegate模式为例(viewcontroller和view之间就是代理模式,viewcontroller有view的使用权,viewcontroller同时 ...

  8. iOS之weak和strong、懒加载及循环引用

    一.weak和strong 1.理解 刚开始学UI的时候,对于weak和strong的描述看得最多的就是“由ARC引入,weak相当于OC中的assign,但是weak用于修饰对象,但是他们都不会造成 ...

  9. 【转】iOS学习之容易造成循环引用的三种场景

    ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是——循环引用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都同 ...

  10. 【原】iOS容易造成循环引用的三种场景,就在你我身边!

    ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是——循环引用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都同 ...

随机推荐

  1. 谷歌浏览器配置vue调试工具

    1.下载调试工具 下载地址:Vue Devtools_6.1.4_chrome扩展插件下载_极简插件 点击推荐下载 2.解压下载的压缩文件: 3.打开chrome浏览器,进入chrome://exte ...

  2. Django笔记二十四之数据库函数之比较和转换函数

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十四之数据库函数之比较和转换函数 这一篇笔记开始介绍几种数据库函数,以下是几种函数及其作用 Cast 转换类型 Coalesce 优先取 ...

  3. 原来这就是所谓的 JSR!

    相信大家在学习 Java 的过程中,或多或少都见过 JSR 这个词.本篇文章就科普下什么是 JSR. 什么是 JSR ? JSR(Java Specification Requests),是指 Jav ...

  4. 5221. 【GDOI2018模拟7.10】A

    题目大意: 给你一棵有根树,问你在这棵树上总共有多少棵子树的节点构成了一个完整的整数区间. 考试想法: 考试时就想到了正解,正解就是从下到上遍历整一棵树,每一个节点记录一下它的最小值min.最大值ma ...

  5. 基于django+ansible+webssh运维自动化管理系统

    基于django+ansible+webssh运维自动化管理系统   前言 最初开发这个基于Django ansible运维自动化管理系统的想法其实从大学时候就已经有了,但是苦于技术原因和没有线上环境 ...

  6. pytes中fixture的scope: 决定可以在什么范围内共享fixture

    1fixture的scope 在@pytest.fixture(scope='xxx')中,scope的可选值有5个,以下是官网的描述 2 function级别的scope 添加如下代码到pytest ...

  7. JVM面试和学习中需要注意的部分

    内存结构 1.方法区用来存储类加载的数据,例如类的名称,方法入口 2.JVM虚拟机栈用于存储线程,包括局部变量和方法参数 3.堆内存用来存储对象 4.方法区的规范实现:永久代和元空间 5.方法区 JV ...

  8. 2021-04-15:给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果。

    2021-04-15:给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果. 福大大 答案2021-04-15: "b"和&q ...

  9. 2021-11-28:有一棵树,给定头节点h,和结构数组m,下标0弃而不用。 比如h = 1, m = [ [] , [2,3], [4], [5,6], [], [], []]

    2021-11-28:有一棵树,给定头节点h,和结构数组m,下标0弃而不用. 比如h = 1, m = [ [] , [2,3], [4], [5,6], [], [], []], 表示1的孩子是2. ...

  10. mysql 新建数据库 排序规则

    utf8_unicode_ci和utf8_general_ci对中.英文来说没有实质的差别.utf8_general_ci校对速度快,但准确度稍差.utf8_unicode_ci准确度高,但校对速度稍 ...