iOS内存管理3 - NSTimer循环引用

NSTimer提供的timerWithTimeInterval:target:selector,该方法中target会对self进行强引用

    self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(fireHome) userInfo:nil repeats:YES];
   [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

解决方式1

    self.timer =[NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
        
    }];

解决方法二:
使用VC中的didMoveToParentViewController释放timer

- (void)didMoveToParentViewController:(UIViewController *)parent{
    // 无论push 进来 还是 pop 出去 正常跑
    // 就算继续push 到下一层 pop 回去还是继续
    if (parent == nil) {
       [self.timer invalidate];
        self.timer = nil;
        NSLog(@"timer 走了");
    }
}

解决方法三:
虚基类NSProxy

@interface LGProxy : NSProxy
+ (instancetype)proxyWithTransformObject:(id)object;
@end

#import "LGProxy.h"

@interface LGProxy()
@property (nonatomic, weak) id object;
@end

@implementation LGProxy
+ (instancetype)proxyWithTransformObject:(id)object{
    LGProxy *proxy = [LGProxy alloc];
    proxy.object = object;
    return proxy;
}

// 通过消息转发机制,让实际的响应target还是外部self,这一步至关重要,主要涉及到runtime的消息机制。
// 转移
// 强引用 -> 消息转发

-(id)forwardingTargetForSelector:(SEL)aSelector {
    return self.object;
}

@end

使用

-(void)viewDidLoad{
    [super viewDidLoad];
    self.proxy = [LGProxy proxyWithTransformObject:self];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.proxy selector:@selector(fireHome) userInfo:nil repeats:YES];
}

- (void)dealloc{
    // VC -> proxy <- runloop
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"%s",__func__);
}

你可能感兴趣的:(iOS内存管理3 - NSTimer循环引用)