原因:

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. defineProperty在数据劫持后是如何通知数据的更新和视图的更新的

    vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和ge ...

  2. Tomcat长轮询原理与源码解析

    Tomcat长轮询原理与源码解析 系列文章目录和关于我 零丶长轮询的引入 最近在看工作使用到的diamond配置中心原理,发现大多数配置中心在推和拉模型上做的选择出奇的一致选择了基于长轮询的拉模型 基 ...

  3. Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享)

    Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享) 常用的两款AI可视化交互应用比较: Gradio Gradio的优势在于易用性,代码结构相比Str ...

  4. 笔记:C++学习之旅---引用

    笔记:C++学习之旅---引用 什么是引用? 引用就是别名,引用并非对象,相反的,他只是为一个已经存在的对象所起的另外一个名字. /*引用就是别名*/ #include <iostream> ...

  5. 笔记五:进程间的通信(IPC通信之共享内存)

    IPC通信 IPC通信(Inter-Process Communication) 三种: 共享内存.消息队列.信号灯 这个IPC对象,肯定是存在于内核中.而且用户空间的文件系统中有没有IPC的文件类型 ...

  6. class(类)和构造函数(原型对象)

    构造函数和class的关系,还有面向对象和原型对象,其实很多人都会很困惑这些概念,这是第二次总结这些概念了,之前一次,没有class类,其实了解了构造函数,class也就很容易理解了 一. 构造函数和 ...

  7. node可以用nvm快速切换版本,golang如何快速切换版本?用gvm就行。

    使用 gvm 可以带来以下好处: 快速切换 Golang 版本,方便进行版本测试和开发: 可以在多个项目中同时使用不同版本的 Golang 包和工具,避免冲突: 可以通过 gvm 管理不同版本的 Go ...

  8. LeetCode 周赛 345(2023/05/14)体验一题多解的算法之美

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 往期回顾:LeetCode 双周赛第 104 场 · 流水的动态规划,铁打的结构化思考 周赛概览 T1. 找 ...

  9. uni-app 创建项目及目录结构

    文件-新建-1.项目 ┌─uniCloud 云空间目录,阿里云为uniCloud-aliyun,腾讯云为uniCloud-tcb(详见uniCloud) │─components 符合vue组件规范的 ...

  10. 痞子衡嵌入式:MCUBootUtility v5.0发布,初步支持i.MXRT1180

    -- 痞子衡维护的NXP-MCUBootUtility工具距离上一个大版本(v4.0.0)发布过去4个多月了,期间痞子衡也做过两个小版本更新,但不足以单独介绍.这一次痞子衡为大家带来了全新大版本v5. ...