iOS-解决NSTimer引起的内存泄漏问题

pop timer所在的控制器,会发现timer还在运行,因为tiemr创建加入了runloop中,如果不主动的调用 invalidate 方法,timer是不会被释放掉的,根本停不下来。

重写dealloc方法,发现pop不走dealloc方法,所以在dealloc中调用invalidate也是没有用的。

控制器要释放掉,则需释放掉所有的实例变量,释放timer,但是释放掉timer则必须释放掉它持有target,由于初始化时timer的target设置为了self,就是当前控制器,所以造成了循环引用,内存泄漏了。

解决办法就是:解除timer对self的强引用,把target替换成单独的对象,再利用消息转发/消息传递使得单独对象中的timer的SEL方法在控制器中实现

方法一:使用NSProxy解决NSTimer内存泄漏问题,利用消息转发来断开NSTimer对象与视图之间的引用关系


@interface TimerProxy : NSProxy



- (instancetype)initWithTarget:(id)target;

@end

#import "TimerProxy.h"

@interface TimerProxy ()

@property (nonatomic,weak)id target;


@end

@implementation TimerProxy


- (instancetype)initWithTarget:(id)target {
    
    self.target = target;

    return self;
}



/*
 这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}

/*
 将消息转发给其他对象,这里转发给控制器
 */
- (void)forwardInvocation:(NSInvocation *)invocation {
    SEL sel=[invocation selector];
    if ([self.target respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.target];
    }
}

使用方法:


- (void)proxy {
    
    TimerProxy *proxy=[[TimerProxy alloc]initWithTarget:self];
    
    timer =[NSTimer timerWithTimeInterval:1 target:proxy selector:@selector(timeAction) userInfo:nil repeats:YES];
    
    [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

}
- (void)timeAction {
    time++;
    
    self.timeLabel.text=[NSString stringWithFormat:@"%d",time];
    
    NSLog(@"time  %d",time);

}
- (void)dealloc {
    [timer invalidate];
}

方法二:使用 消息传递 解决NSTimer内存泄漏问题
创建一个对象,让NSTimer对这个对象进行强引用,而不对控制器进行强引用


@interface WeakTimeObject : NSObject

+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)sel userInfo:(id)userInfo repeats:(BOOL)isRepeats;

@end

@interface WeakTimeObject ()

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

@end

@implementation WeakTimeObject


+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)sel userInfo:(id)userInfo repeats:(BOOL)isRepeats {
    
    WeakTimeObject *objc=[[WeakTimeObject alloc]init];
    objc.target = target;
    objc.selector = sel;
    
    
    //Nstimer 对 WeakTimeObject 对象 强引用
    return [NSTimer scheduledTimerWithTimeInterval:timeInterval target:objc selector:@selector(timeAction:) userInfo:userInfo repeats:isRepeats];
}


//消息传递,在 self.target  运行 self.selector 方法
- (void)timeAction:(id)info {
    [self.target performSelector:self.selector withObject:info];
    
}

使用方法

- (void)objc {
    timer = [WeakTimeObject weakScheduledTimerWithTimeInterval:1.0 target:self selector:@selector(update) userInfo:nil repeats:YES];

}
- (void)update {
    time++;
    
    self.timeLabel.text=[NSString stringWithFormat:@"%d",time];
    
    NSLog(@"time  %d",time);
}

- (void)dealloc {
    [timer invalidate];
}

你可能感兴趣的:(iOS-解决NSTimer引起的内存泄漏问题)