打破NSTimer循環(huán)引用的方案

NSTimer相信大家經(jīng)常都會(huì)用到,但是,如果不注意的話,很容易就會(huì)造成內(nèi)存泄漏。
我們先來暫時(shí)一個(gè)例子:

@interface HomeControllerViewController ()

@property(nonatomic, strong)NSTimer *timer;

@end

@implementation HomeControllerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor redColor];
    
    self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerSchedue:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)timerSchedue:(NSTimer *)timer {
    
    static int i = 0;
    NSLog(@"%d",i);
    i++;
}

- (void)dealloc {
    
    NSLog(@"已經(jīng)銷毀HomeControllerViewController");
}

@end

如果你從A controller推進(jìn)一個(gè)Homecontroller,然后再點(diǎn)返回,那么造成的現(xiàn)象就是,一直打印1,2,3......不會(huì)停止,直到你殺掉進(jìn)程。并且,NSLog(@"已經(jīng)銷毀HomeControllerViewController");這句不會(huì)打印。

原因分析
為什么會(huì)這樣呢?
可能有人會(huì)猜:既然不走delloc方法,那肯定就是有循環(huán)引用在,導(dǎo)致不能釋放HomeControllerViewController,那么,我把timer的target傳weakSelf,會(huì)不會(huì)解決這個(gè)問題呢?
經(jīng)過試驗(yàn)之后,答案是,并不能,還是不走delloc方法。

那么,我們要怎么解決這個(gè)問題呢?
我們先來看看文檔:


image.png

文檔上有提到,The receiver retains a Timer。很明顯,接收者會(huì)強(qiáng)引用timer,那么你穿weakSelf就沒什么用了。

image.png

如果我們采取一個(gè)proxy類,把他的引用斷開呢?


image.png

這樣是否可以解決這個(gè)問題呢?按原理來說是應(yīng)該可以的。我們來嘗試下:
我們先創(chuàng)建一個(gè)weakProxy類:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface WeakProxy : NSProxy

@property (weak,nonatomic,readonly)id target;

+ (instancetype)proxyWithTarget:(id)target;
- (instancetype)initWithTarget:(id)target;

@end

NS_ASSUME_NONNULL_END


#import "WeakProxy.h"

@implementation WeakProxy

- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(id)target {
    return [[WeakProxy alloc] initWithTarget:target];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation setReturnValue:&null];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}

- (BOOL)isEqual:(id)object {
    return [_target isEqual:object];
}

- (NSUInteger)hash {
    return [_target hash];
}

- (Class)superclass {
    return [_target superclass];
}

- (Class)class {
    return [_target class];
}

- (BOOL)isKindOfClass:(Class)aClass {
    return [_target isKindOfClass:aClass];
}

- (BOOL)isMemberOfClass:(Class)aClass {
    return [_target isMemberOfClass:aClass];
}

- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    return [_target conformsToProtocol:aProtocol];
}

- (BOOL)isProxy {
    return YES;
}

- (NSString *)description {
    return [_target description];
}

- (NSString *)debugDescription {
    return [_target debugDescription];
}

@end

這個(gè)時(shí)候,我們的timer的target要改一下,改成weakproxy:

self.timer = [NSTimer timerWithTimeInterval:1 target:[WeakProxy proxyWithTarget:self] selector:@selector(timerSchedue:) userInfo:nil repeats:YES];

我們運(yùn)行來看看效果:


image.png

果然,delloc的方法執(zhí)行了,說明,循環(huán)引用被打破了。

所以,后面如果用到NSTimer的時(shí)候,記得要注意這個(gè)問題哦.....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容