NSTimer循環(huán)引用解決方案

文章以在TimerViewController中使用計時器為例,在VC中聲明一個NSTimer屬性。

創(chuàng)建NSTimer對象:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(startTimer) userInfo:nil repeats:YES];

timer作為VC的屬性,被VC強引用,創(chuàng)建timer對象時VC作為target被timer強引用,即循環(huán)引用。

image

解決循環(huán)引用的幾種方式:

weak指針:

既然是強引用導(dǎo)致循環(huán)引用,那么用__weak修飾self就好了,想法是對的,但是做法是無效的。因為無論是weak還是strong修飾,在NSTimer中都會重新生成一個新的強引用指針指向self,導(dǎo)致循環(huán)引用的。

類方法:

創(chuàng)建一個繼承NSObject的子類TempTarget,并創(chuàng)建開啟計時器的方法。

TempTarget .h文件

@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *tempTimer;
@property (nonatomic, weak) id tempTarget;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval target:(id)tempTarget selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats;

TempTarget .m文件

@implementation TempTarget

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval target:(id)tempTarget selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats {
    TempTarget *target = [[TempTarget alloc] init];
    target.tempTarget = tempTarget;
    target.selector = selector;
    target.tempTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:target selector:@selector(timerSelector:) userInfo:userInfo repeats:repeats];
    return target.tempTimer;
}

- (void)timerSelector:(NSTimer *)tempTimer {
    if (self.tempTarget && [self.tempTarget respondsToSelector:self.selector]) {
        [self.tempTarget performSelector:self.selector withObject:tempTimer.userInfo];
    }else {
        [self.tempTimer invalidate];
    }
}

@end

VC調(diào)用

self.timer = [TempTarget scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerStart:) userInfo:nil repeats:YES];

VC強引用timer,因為timer的target是TempTarget實例,所以timer強引用TempTarget實例,而TempTarget實例弱引用VC,解除循環(huán)引用。

image
WeakProxy:

創(chuàng)建一個繼承NSProxy的子類WeakProxy,并實現(xiàn)消息轉(zhuǎn)發(fā)的相關(guān)方法。

WeakProxy.h文件

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

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

WeakProxy.m文件

@implementation WeakProxy

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

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

- (void)forwardInvocation:(NSInvocation *)invocation {
    SEL sel = [invocation selector];
    if ([self.weakTarget respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.weakTarget];
    }
}

//返回方法的簽名。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.weakTarget methodSignatureForSelector:sel];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [self.weakTarget respondsToSelector:aSelector];
}
@end

VC調(diào)用

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[WeakProxy proxyWithTarget:self] selector:@selector(timerStart:) userInfo:nil repeats:YES];
- (void)dealloc {
    [_timer invalidate];
    NSLog(@"VC銷毀了");
}

原理跟類方法相似,打破循環(huán)引用的環(huán)路。將timer的target設(shè)置為WeakProxy實例,利用消息轉(zhuǎn)發(fā)機制實現(xiàn)執(zhí)行VC中的計時方法,解決循環(huán)引用。

GCD:

VC的.m文件

@interface ViewController ()
@property (nonatomic, strong) dispatch_source_t timer;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), 0.1*NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(_timer, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"執(zhí)行了");
        });
    });
    //開啟計時器
    dispatch_resume(_timer);
}

//結(jié)束計時
dispatch_source_cancel(_timer);

Block:

創(chuàng)建NSTimer的分類NSTimer+UnretainCycle。

+ (NSTimer *)lhScheduledTimerWithTimeInterval:(NSTimeInterval)inerval
                                      repeats:(BOOL)repeats
                                        block:(void(^)(NSTimer *timer))block {
    return [NSTimer scheduledTimerWithTimeInterval:inerval target:self selector:@selector(blcokInvoke:) userInfo:[block copy] repeats:repeats];
}

+ (void)blcokInvoke:(NSTimer *)timer {

    void (^block)(NSTimer *timer) = timer.userInfo;
    if (block) {
        block(timer);
    }
}

VC調(diào)用

self.timer = [NSTimer mxScheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer *timer) {
        NSLog(@"執(zhí)行了");
    }];

來自:http://m.itdecent.cn/p/aaf7b13864d9

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

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

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