モバイル製品の安定性NSTimer

6504 ワード

NSTimerは日常の仕事でよく使われています.いくつかの面でよく使われています
  • タイミング繰返しタスク
  • 遅延タスク
  • Banner運用コントロールスライド
  • 繰り返しリフレッシュインターフェース
  • これらの面では、ほとんどのアプリケーションが直面しているニーズですが、NSTimerには間接的に多くのcrashを招き、これらのcrashがどこのコードによるのか分からない非常に危険な場所があります.
    NSTimerの危険原因
    一般的な開発者はNSTimerを以下のように使用し,runloop関連の操作をカプセル化し,より使いやすいようにscheduleの先頭の方法を使用するのが一般的である.
    @property (nonatomic, strong) NSTimer* timer;
    ...
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
    ...
    - (void)timerDidFire:(NSTimer*)timer
    {
        NSLog(@"%@", @"timerDidFire:");
    }

    以上のコードは,非常に正常なNSTimer実装方式である.しかし問題はtargetがselfであることです.システムのNSTimer関数に注目する必要があります.strongがselfに住んでいますか??実験により,システムは確かにselfにstrongが住んでおり,これにより循環参照とメモリ漏洩を招くことが分かった.
    A classでは、NSTimerが継続的に実行されているTBTimerTestObjectを定義します.
    - (BOOL)TestTimterInAClass
        _obj   = [[TBTimerTestObject alloc] init];
        [self performSelector:@selector(timerDidFired:) withObject:nil afterDelay:1];
        return YES;
    }
    - (void)timerDidFired:(NSTimer*)timer
    {
        _obj = nil;
        NSLog(@"%@", @" AppDelegate timerDidFired");
    }

    TBTimerTestObjectファイル
    @interface TBTimerTestObject ()
    @property (nonatomic, weak) NSTimer* timer;
    @end
    @implementation TBTimerTestObject
    - (void)dealloc
    {
        NSLog(@"sssss");
    }
    - (id)init
    {
        self  = [super init];
    
        if (self) {
            _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
        }
        return self;
    }
    - (void)timerDidFire:(NSTimer*)timer
    {
        NSLog(@"%@", @"1111111");
    }
    @end

    実行コードの結果
    11111111
    AppDelegate timerDidFired
    11111111
    11111111
    11111111
    11111111

    結果としてメモリリークがある
    NSTimerの隠れた危険がもたらす危害
    一般的に、メモリの漏洩が発生しても大きな問題は発生しませんが、以前のtimerオブジェクトのため、内部がうまく処理されていないため、timerdidfiredが絶えず実行されると、深刻な危険が発生し、crashも発生します.
  • 共有メモリ処理
  • NSArray/NSDictoryに対する処理
  • webに対する処理
  • そのため、メモリの漏洩の問題を解決するには、非常に重要です.
    ソリューション
    NSTimerメモリの漏洩の問題では、targetがselfをhookし、selfとtimerのループ参照を引き起こすことが重要です.では、解決したい方法は、ループリファレンスの問題を解決することだけです.
    #import <Foundation/Foundation.h>
    @interface TBWeakTimerTarget : NSObject
    - (instancetype) initWithTarget: (id)target andSelector:(SEL) selector;
    - (void)timerDidFire:(NSTimer *)timer;
    @end
    
    #import "TBWeakTimerTarget.h"
    @implementation TBWeakTimerTarget
    {
        __weak id _target;
        SEL _selector;
    }
    - (instancetype) initWithTarget: (id) target andSelector: (SEL) selector
    {
        self = [super init];
        if (self) {
            _target = target;
            _selector = selector;
        }
        return self;
    }
    - (void) dealloc
    {
        NSLog(@"TBWeakTimerTarget dealloc");
    }
    - (void)timerDidFire:(NSTimer *)timer
    {
        if(_target)
        {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [_target performSelector:_selector withObject:timer];
    #pragma clang diagnostic pop
        }
        else
        {
            [timer invalidate];
        }
    }
    @end

    使用方法:
    TBWeakTimerTarget* timerTarget = [[TBWeakTimerTarget alloc] initWithTarget:self andSelector:@selector(timerDidFire:)];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:timerTarget selector:@selector(timerDidFire:) userInfo:nil repeats:YES];

    うん、簡単だよ.TBWeakTimerTargetがNSTimerの使用を保護すると、NSTimerとSelfが循環参照しないことが保証されます.
    要するに、モバイル製品の安定性は製品性能の重要な指標である.
    【叁省http://blog.csdn.net/bjtufang 転載は出典を明記し、労働成果を尊重してください.