ios - NSTimer中target的self是強引用問題


  • 當控制器ViewController跳轉進入控制器OneViewController中的時候開啟定時器,讓定時器每隔一段時間打印一次,當OneViewController dismiss的時候,控制器並沒有被銷毀.然而定時器的timer invalidate 在dealloc中已經寫了.

  • 如果沒有定時器,則OneViewController可以正常銷毀.

  • 原因在於下圖:循環引用

  • 控制器ViewController跳轉進入OneViewController中開啟定時器



#import "OneViewController.h"

@interface OneViewController ()


@property (nonatomic, strong) NSTimer *timer;

@end


@implementation OneViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self dismissViewControllerAnimated:YES completion:nil];
}

-(void)viewDidLoad{

    [super viewDidLoad];
    self.view.backgroundColor = [UIColor orangeColor];
    
    /**
     1.__weak typeof(self) weakSelf = self; 不能解決

     */
    
    //開啟定時器 
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(testTimerDeallo) userInfo:nil repeats:YES];
}

/** 方法一直執行 */
-(void)testTimerDeallo{

    NSLog(@"-----");
}

  • 當開啟定時器以后,testTimerDeallo方法一直執行,即使dismiss此控制器以后,也是一直在打印,而且dealloc方法不會執行.循環引用造成了內存泄露,控制器不會被釋放.

/** 開啟定時器以后控制器不能被銷毀,此方法不會被調用 */
-(void)dealloc{

    NSLog(@"xiaohui");
    [self.timer invalidate];
}


@end



  • 解決辦法: 由於循環引用的起因是target,則可以包裝一個target,讓target是另一個對象,而不是ViewController即可.
  • 1.創建一個集成NSObject的分類TimerWeakTarget,創建類方法---開啟定時器的方法

#import <Foundation/Foundation.h>

@interface TimerWeakTarget : NSObject

@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, weak) id target;


/** 
 1.重寫開啟定時器方法,在內部對target進行替換,換成本類(TimerWeakTarget)的對象即可
 2.不會造成循環引用了,原控制器OneViewController屬性有timer對timer強應用,timer內部對self強引用,但是self在此方法內部被替換成了本類的對象(TimerWeakTarget *),而本類的對象不會對OneViewController強引用,則不會造成循環引用,也就不會造成內存泄露
 */
+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats;

@end

  • TimerWeakTarget.m文件中
  • 在下面我們封裝的類的方法中,我們將開啟定時器的方法 [NSTimer scheduledTimerWithTimeInterval:interval target:timer selector:@selector(fire:) userInfo:userInfo repeats:repeats];中的target換掉了,換成了 本類的對象,timer.在OneViewController中開啟定時器的時候直接調用這個類方法,就不會造成循環引用.看圖


#import "TimerWeakTarget.h"

@implementation TimerWeakTarget

+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats{

    TimerWeakTarget * timer = [TimerWeakTarget new];
    timer.target = aTarget;
    timer.selector = aSelector;
    //-------------------------------------------------------------此處的target已經被換掉了不是原來的VIewController而是TimerWeakTarget類的對象timer
    timer.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:timer selector:@selector(fire:) userInfo:userInfo repeats:repeats];
    return timer.timer;
}



-(void)fire:(NSTimer *)timer{

    if (self.target) {
        [self.target performSelector:self.selector withObject:timer.userInfo];
    } else {
    
        [self.timer invalidate];
    }
}

@end

  • 控制器dismiss以后可以正常被銷毀.問題解決.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM