zoukankan      html  css  js  c++  java
  • iOS 循环引用的问题总结

    原因:

    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进行强引用,还是会产生循环引用。
  • 相关阅读:
    2017-2018-1 20155323 《信息安全系统设计基础》第十三周学习总结
    2017-2018-1 20155323 实验五 通讯协议设计
    2017-2018-1 20155323 20155314   实验四  外设驱动程序设计
    2017-2018-1 20155323 《信息安全系统设计基础》第11周学习总结
    IPC学习
    2017-2018-1 20155323 《信息安全系统设计基础》第10周学习总结
    2017-2018-1 20155323 《信息安全系统设计基础》第9周学习总结
    2017-2018-1 20155323 实验三 实时系统
    20155320《信息安全系统设计基础》课程总结
    2017-2018-1 20155320 《信息安全系统设计基础》第十四周学习总结
  • 原文地址:https://www.cnblogs.com/liuwenqiang/p/13214275.html
Copyright © 2011-2022 走看看