iOS崩溃处理机制:NSTimer Crash防护

产生的原因:
NSTimer会 强引用 target实例,所以需要在合适的时机invalidate 定时器,否则就会由于定时器timer强引用target的关系导致 target不能被释放,造成内存泄露,甚至在定时任务触发时导致crash。与此同时,如果NSTimer是无限重复的执行一个任务的话,也有可能导致target的selector一直被重复调用且处于无效状态,对app的CPU,内存等性能方面均是没有必要的浪费。所以,很有必要设计出一种方案,可以有效的防护NSTimer的滥用问题。

解决方案:
定义一个抽象类,NSTimer实例强引用抽象类,而在抽象类中,弱引用target,这样target和NSTimer之间的关系也就是弱引用了,意味着target可以自由的释放,从而解决了循环引用的问题。

具体方式:
1、定义一个抽象类,抽象类中弱引用target。

#import 

NS_ASSUME_NONNULL_BEGIN

@interface XZProxy : NSProxy

+ (instancetype)proxyWithTarget:(id)target;

@end

NS_ASSUME_NONNULL_END
#import "XZProxy.h"


@interface XZProxy ()

/// 消息转发的对象
@property (nonatomic, weak) id target;

@end

@implementation XZProxy

+ (instancetype)proxyWithTarget:(id)target {
    // NSProxy没有init方法, 只需要调用alloc创建对象即可
    XZProxy *proxy = [XZProxy alloc];
    proxy.target = target;
    return proxy;
}

- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}

@end

2、创建category,交换系统方法,实现NSTimer强引用抽象类。
ps:也可以不使用分类,不用交换方法,直接在创建timer实例的时候,将原本的target指向抽象类即可

#import 

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (NSTimerCrash)

+ (void)xz_enableTimerProtector;

@end

NS_ASSUME_NONNULL_END
#import "NSObject+NSTimerCrash.h"
#import "NSObject+XZSwizzle.h"
#import "XZProxy.h"

@implementation NSObject (NSTimerCrash)


+ (void)xz_enableTimerProtector {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        // 创建一个timer并把它指定到一个默认的runloop模式中,并且在 TimeInterval时间后 启动定时器
        [NSTimer xz_classSwizzleMethod:@selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:) replaceMethod:@selector(xz_scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:)];
        
        // 创建一个定时器,但是么有添加到运行循环,我们需要在创建定时器后手动的调用 NSRunLoop 对象的 addTimer:forMode: 方法。
        [NSTimer xz_classSwizzleMethod:@selector(timerWithTimeInterval:target:selector:userInfo:repeats:) replaceMethod:@selector(xz_timerWithTimeInterval:target:selector:userInfo:repeats:)];
    });
}


+ (NSTimer *)xz_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats {
    
    return [self xz_scheduledTimerWithTimeInterval:timeInterval target:[XZProxy proxyWithTarget:target] selector:selector userInfo:userInfo repeats:repeats];
}

+ (NSTimer *)xz_timerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo {
    
    return [self xz_timerWithTimeInterval:timeInterval target:[XZProxy proxyWithTarget:target] selector:aSelector userInfo:userInfo repeats:yesOrNo];
}

@end

你可能感兴趣的:(iOS崩溃处理机制:NSTimer Crash防护)