iOS-解決NSTimer強引用

前言git

NSTimer的官方文檔對於target的解釋,The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.意思就是會對target強引用,直到timer被invalidated掉。github

 

場景:ide

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(update:) userInfo:nil repeats:YES];控制器強引用timer,timer強引用控制器,形成循環引用,致使控制器沒法釋放形成內存泄露。oop

 

weakSelf:atom

__weak typeof(self) weakSelf = self;spa

    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:weakSelf selector:@selector(update:) userInfo:nil repeats:YES];code

這種簡單的方式,並不能打破timer和控制器之間的循環引用,也就是說weakSelf和strongSelf在這裏的惟一區別:就是self被釋放了,target會變成nil。ip

 

NSProxy構建中間target:內存

DYLWeakProxy對target進行弱引用,而後經過消息轉發,最後的執行者轉發至targetObject,由於target是弱引用,所以不會形成循環引用。ci

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:[DYLWeakProxy weakProxyForObject:self] selector:@selector(update:) userInfo:nil repeats:YES];

#import <Foundation/Foundation.h>

@interface DYLWeakProxy : NSProxy

+ (instancetype)weakProxyForObject:(id)targetObject;

@end


#import "DYLWeakProxy.h"

@interface DYLWeakProxy ()

@property (weak, nonatomic) id targetObject;

@end

@implementation DYLWeakProxy

+ (instancetype)weakProxyForObject:(id)targetObject
{
    DYLWeakProxy *weakObject = [DYLWeakProxy alloc];
    weakObject.targetObject = targetObject;
    return weakObject;
}

#pragma mark - Forwarding Messages

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.targetObject;
}


#pragma mark - NSWeakProxy Method Overrides
#pragma mark - Handling Unimplemented Methods

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
   return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{    
    void *nullPointer = NULL;
    [invocation setReturnValue:&nullPointer];
}

@end

 

CADisplayLink:

一樣的,CADisplayLink也會由於強引用target,從而形成循環引用,解決這個問題與NSTimer相似,依然能夠DYLWeakProxy解決;其實加載gif的第三方FLAnimatedImage也使用該方法解決循環引用的問題。

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:[DYLWeakProxy weakProxyForObject:self] selector:@selector(update:)];

 

最後

除了NSTimer定時器和CADisplayLink外,GCD也能夠建立定時器(我封裝的DYLDispatchTimerButton倒計時用的就是GCD定時器),並且不存在循環應用的問題以及RunLoop的問題,NSTimer默認狀況下運行在NSDefaultRunLoopMode模式下,所以爲了防止滾動視圖在滾動過程當中NSTimer也能正常運行,會經過設置NSRunLoopCommonModes標記,自動的將NSTimer加入到帶有CommonMode標記的模式下,即NSDefaultRunLoopMode和NSEventTrackingRunLoopMode。

相關文章
相關標籤/搜索