以下代碼,dealloc永遠不會走,由於self引用timer,timer引用了target(self = BlockViewController2),形成了相互強引用ios
@interface BlockViewController2 ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation BlockViewController2
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(onTimer) userInfo:nil repeats:YES];
}
- (void)onTimer {
NSLog(@"-- timer --");
}
- (void)dealloc {
NSLog(@"dealloc 2 = %@", self);
[self.timer invalidate];
}
@end
複製代碼
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
複製代碼
這個方法能解決循環引用問題,但可能致使新問題,好比在A vc 使用timer,在A push B的時候,釋放timer,此時從b pop 回A,A timer已經不存在,須要從新建立macos
/// Creates and returns a new NSTimer object initialized with the specified block object. This timer needs to be scheduled on a run loop (via -[NSRunLoop addTimer:]) before it will fire.
/// - parameter: timeInterval The number of seconds between firings of the timer. If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead
/// - parameter: repeats If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
/// - parameter: block The execution body of the timer; the timer itself is passed as the parameter to this block when executed to aid in avoiding cyclical references
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
/// Creates and returns a new NSTimer object initialized with the specified block object and schedules it on the current run loop in the default mode.
/// - parameter: ti The number of seconds between firings of the timer. If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead
/// - parameter: repeats If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
/// - parameter: block The execution body of the timer; the timer itself is passed as the parameter to this block when executed to aid in avoiding cyclical references
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
複製代碼
self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"-- timer -- ");
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
複製代碼
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"-- timer -- ");
}];
複製代碼
使用這兩個方法的時候須要注意避免Block的循環引用markdown
由於NSTimer循環引用的核心問題是timer 的target引用了self,咱們對target換成封裝類的對象,這樣能夠解決timer引用self的問題app
CCTimer.hless
@interface CCTimer : NSObject
- (void)startTimer;
- (void)stopTimer;
@end
複製代碼
CCTimer.moop
#import "CCTimer.h"
@implementation CCTimer
{
NSTimer * _timer;
}
- (void)startTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimer) userInfo:nil repeats:YES];
}
- (void)stopTimer {
if (_timer == nil) {
return;
}
[_timer invalidate];
_timer = nil;
}
- (void)onTimer {
NSLog(@"-- timer --");
}
- (void)dealloc {
NSLog(@"-- timer dealloc -- ");
}
@end
複製代碼
調用:this
- (void)viewDidLoad {
[super viewDidLoad];
self.ccTimer = [[CCTimer alloc] init];
[self.ccTimer startTimer];
}
- (void)onTimer {
NSLog(@"-- timer --");
}
- (void)dealloc {
NSLog(@"dealloc 2 = %@", self);
[self.ccTimer stopTimer];
[self.timer invalidate];
}
複製代碼
這樣的方式主要是讓CCTimer
強引用NSTimer
, NSTimer
強引用CCTimer
,避免與ViewController強引用, 在ViewController
dealloc
方法中釋放timer
對NSTimer
進行銷燬,CCTimer
也就會相應的銷燬,避免了循環引用atom
@interface CCProxy : NSProxy
- (instancetype)initWithObject:(id)obj;
+ (instancetype)proxyWithObject:(id)obj;
@end
複製代碼
//
// CCProxy.m
// MemoryManageDemo
//
// Created by Ternence on 2021/5/17.
//
#import "CCProxy.h"
@interface CCProxy ()
@property (nonatomic, weak) id obj;
@end
@implementation CCProxy
- (instancetype)initWithObject:(id)obj {
self.obj = obj;
return self;
}
+ (instancetype)proxyWithObject:(id)obj {
return [[self alloc] initWithObject:obj];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.obj respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget: self.obj];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.obj methodSignatureForSelector:sel];
}
@end
複製代碼
調用:spa
CCProxy *proxy = [[CCProxy alloc] initWithObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:proxy selector:@selector(onTimer) userInfo:nil repeats:YES];
複製代碼
經過CCProxy這個僞基類(至關於ViewContoller的複製類),避免讓timer引用ViewControllercode