iOS | 用於解決迴圈引用的block timer

weixin_34006468發表於2018-12-05
1692043-5f65f38ecc2082ec.png

iOS 10的時候NSTimer新增了一個帶block的API:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

蘋果的官方文件裡說,將這個timer本身作為引數傳給block以此來避免迴圈引用:

/// - parameter: block The execution body of the timer; the timer itself is passed as the parameter to this block when executed to aid in avoiding cyclical references

有了這個API再也不需要繁瑣的手動登出timer,結合weakSelf就可以輕鬆處理迴圈引用,如:

__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
    __strong typeof(self) strongSelf = weakSelf;
    [strongSelf printNum];
}];

在這個API出現之前,self和timer的引用關係是:
self->timer->self

現在的引用關係是:
self->timer->weakSelf

但是隻有iOS 10及之後的系統才能使用此API,而我們一般都是適配到iOS 8,所以有必要擴充套件一下。

如何擴充套件?

簡單點,寫個category,直接複製蘋果的API進去(思考API設計的時間都省了?),然後加上字首:

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

+ (void)cq_callBlock:(NSTimer *)timer {
    void (^block)(NSTimer *timer) = timer.userInfo;
    !block ?: block(timer);
}

你不是把timer作為引數傳給block嗎?那我也這樣搞。

然後就可以像使用系統API那樣使用了:

__weak typeof(self) weakSelf = self;
self.timer = [NSTimer cq_scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer *timer) {
    __strong typeof(self) strongSelf = weakSelf;
    [strongSelf printNum];
}];

最後提供一個此timer使用的具體demo:
https://github.com/CaiWanFeng/CQCountDownButton

相關文章