原因:

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

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

- (void)dealloc{
[_timer invalidate];
NSLog(@"********************delloc**************8");
}

解决方式:

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

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

2.手动破解循环

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

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

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

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

4.消息转发机制

创建一个中间类 PHJProxy,


@interface PHJProxy : NSObject

@property (nonatomic, weak) id target;

@end


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

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

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

@interface LWProxy : NSProxy

@property (nonatomic, weak) id  target;

+ (instancetype)proxyWithTarget:(id)target;

@end

/**
NSProxy 类,是专门做消息转发的代理类,如果本类中没有方法实现,则不会去父类中查找,直接进入 -()methodSignatureForSelector{},进行转发
因此此类做消息转发比NSObject 效率更高
*/
@implementation LWProxy + (instancetype)proxyWithTarget:(id)target
{
// 只有alloc方法,没有init方法
LWProxy *proxy = [LWProxy alloc];
proxy.target = target;
return proxy;
} - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
} - (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
} @end

5. NSTimer 分类的方式,

@implementation NSTimer (LWTimer)
+ (NSTimer *)lw_timerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(timermethod:) userInfo:[block copy] repeats:YES];
} + (void)timermethod:(NSTimer *)timer
{
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
@end

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

使用方式:

__weak typeof(self)weakself = self;

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

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

注意:

1、把timer改成弱引用

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

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

__weak typeof(self) weakSelf = self;

 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. day05 运算符和流程控制

    Day05 逻辑运算符 成员运算符 身份运算符 流程控制(重点) if判断 while循环 标志位的使用 逻辑运算符 and  与 #可以用and链接多个条件,会按照从左到右的顺序依次判断,有一个为F ...

  2. Numpy浅拷贝与深拷贝

    Numpy中的浅拷贝与深拷贝 浅拷贝 共享内存地址的两个变量,当其中一个变量的值改变时另外一个也随之改变. Example a = np.array([1, 2, 3, 4, 5]) b = a pr ...

  3. MySQL(四)用户与权限管理

    用户与权限管理 用户管理 MySQL用户分为普通用户和root用户,提供了许多语句来管理包括登录.退出MySQL服务器.创建用户.删除用户.密码管理和权限管理等内容. 登录MySQL服务器 mysql ...

  4. Portainer安装

    个人博客地址: https://note.raokun.top 拥抱ChatGPT,国内访问网站:https://www.playchat.top Portainer是一个可视化的容器镜像的图形管理工 ...

  5. CSS6大种选择器

    一.常用的css基本选择器(4种) 1.标签选择器 结构: 标签名{css属性名:属性值}作用:通过标签名,找到页面中所有的这类标签,设置样式 注意:1.标签选择器选择的是一类标签,而不是单独的一个2 ...

  6. 基于QtAV的简易播放器(开源)

    这个开源代码,是我利用QtAV源码,提取其中一部分代码,进行整合到我自己项目中,做的一个小型播放器测试,至于怎么安装一些环境以及QtAV源码编译在我以前写的一篇博客中可以看到(Qt第三方库QtAV-- ...

  7. [C++提高编程] 2、STL初识

    文章目录 2 STL初识 2.1 STL的诞生 2.2 STL基本概念 2.3 STL六大组件 2.4 STL中容器.算法.迭代器 2.5 容器算法迭代器初识 2.5.1 vector存放内置数据类型 ...

  8. 数据剖析更灵活、更快捷,火山引擎 DataLeap 动态探查全面升级

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 近期,火山引擎 DataLeap 上线"动态探查"能力,为用户提供全局数据视角.完善的抽样策略 ...

  9. selenium 多浏览器处理

    开头 当我们做多浏览器自动化测试的时候,需要很多不同的浏览器,所以不同的参数对应不同的浏览器操作 目录结构 新建一个 base.py 文件, 通过 os 捕获到从命令行传进来的 import pyte ...

  10. 2022-09-01:字符串的 波动 定义为子字符串中出现次数 最多 的字符次数与出现次数 最少 的字符次数之差。 给你一个字符串 s ,它只包含小写英文字母。请你返回 s 里所有 子字符串的 最大波

    2022-09-01:字符串的 波动 定义为子字符串中出现次数 最多 的字符次数与出现次数 最少 的字符次数之差. 给你一个字符串 s ,它只包含小写英文字母.请你返回 s 里所有 子字符串的 最大波 ...