使用NSTimer出現的問題

使用NSTimer出現的問題


去年封的一個圖片輪播的, 這兩天在忙着給從新封裝一下, 增長更多的方法, 有更多個性化的設置, 增長了網絡請求圖片的輪播. 從新封裝, 這個過程還算順利, 可是到計時器那塊卡住了.git

WHImagePlayer圖片輪播和圖片瀏覽器: https://github.com/hell03W/WHImagePlayer, 歡迎star支持一下, 若是有任何問題歡迎issue給我.github

問題是這樣的:瀏覽器

簡單來講, 其實就是計時器, 我用的NSTimer, 是在一個自定義view中建立NSTimer的, 若是是在控制器中, 能夠在viewWillDisappear:中執行NSTimer的invalidate方法, timer對象是能夠被正常銷燬的, 可是在自定義的view裏面沒有那樣的方法! 有人會說能夠將target設置成__weak類型的就能夠了嘛 ! 我也是那樣認爲的, 但是試驗證實, 那樣是不行的(要是那麼簡單, 也不必寫個blog來記錄了).網絡

通過在網上查找資料, 和本身的思考, 最終總結了幾種可行方案:app

方案一:oop

以下代碼所示, 使用dispatch_after能夠完美解決, 可是須要注意的是, 在block內部, 使用self時候必須使用weakSelf, 若是直接使用self, 仍是不會被釋放掉的.spa

__weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [UIView animateWithDuration:0.3 animations:^{
            weakSelf.imageScrollView.contentOffset = CGPointMake(weakSelf.width*2, 0);
        } completion:^(BOOL finished) {
            if (finished) {
                [weakSelf scrollViewDidEndDecelerating:weakSelf.imageScrollView];
                [weakSelf setupTimer];
            }
        }];
    });

方案2:code

使用一個假冒的對象做爲target. 原理也不復雜, 建立一個類, 讓新的類擁有self的弱引用, 經過這個類的方法, 將須要做爲target的"self"和要執行的方法的名字"selector", 傳入新的類內部, 在新的類內部, 調用"self""selector"方法, 在自定義view的NSTimer方法中調用新的類的調用外部類方法的方法.orm

上代碼:對象

WHTimerTarget.h

#import <Foundation/Foundation.h>

@interface WHTimerTarget : NSObject

/**1. 調用這個方法  傳入target和selector和時間, 就會間隔timeInterval秒執行target的selector方法.
    內部使用NSTimer實現, 主要是爲了解決, NSTimer會出現循環引用的問題
 */
+ (void)addTarget:(id)target selector:(SEL)selector timeInterval:(NSTimeInterval)timeInterval;

/// 2. 若是如要NSTimer寫在本類外面, 可使用這個方法
+ (instancetype)timerTarget:(id)target selector:(SEL)selector;

- (void)timerDidFire:(NSTimer *)timer;

@end

WHTimerTarget.m

#import "WHTimerTarget.h"

@implementation WHTimerTarget
{
    __weak id  _target;
    SEL _selector;
}

- (instancetype)initWithTarget:(id)target selector:(SEL)selector {
    self = [self init];
    _target = target;
    _selector = selector;
    return self;
}

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

- (void)timerDidFire:(NSTimer *)timer
{
    if(_target)
    {
        [_target performSelector:_selector withObject:timer];
    }
    else
    {
        [timer invalidate];
    }
}

+ (void)addTarget:(id)target selector:(SEL)selector timeInterval:(NSTimeInterval)timeInterval
{
    id timerTarget = [[self alloc] initWithTarget:target selector:selector];
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval
                                                      target:timerTarget
                                                    selector:@selector(timerDidFire:)
                                                    userInfo:nil
                                                     repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

@end

如上代碼所示, 就是新的類, 僞target的所有代碼. 使用的方法有兩種, 以下:

1, 使用方法:
 id target = [TimerTarget timerTarget:<#self#> selector:@selector(<#automaticScroll:#>)];
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:<#self.autoScrollTimeInterval#> target:target selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
 
 2, 使用方法:
 [TimerTarget addTarget:<#self#> selector:@selector(<#automaticScroll:#>) timeInterval:<#3#>];

寫在後面的:

目前爲止, 我只發現這兩種比較好用的方法, 若是誰有更好的方案, 歡迎留言交流 .

相關文章
相關標籤/搜索