最近在調(diào)試 NSTimer匀归,發(fā)現(xiàn)了一些有趣的東西,在此文分析研究一下耗帕。
問題
一個 viewController穆端,持有了一個 timer, 當(dāng)不需要這個 timer 的時候,能不能只調(diào)用 self.timer = nil;
仿便?
什么是 NSTimer体啰?
根據(jù) Apple 的開源代碼 CFRunLoop.c 的定義,NSTimer 對應(yīng)著一個 __CFRunLoopTimer 的結(jié)構(gòu)體探越,具體代碼如下:
__CFRunLoopTimer
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
可以看到 __CFRunLoopTimer 里面有一個CFRunLoopRef _runLoop
狡赐,這個表示 timer 是屬于哪一個 runLoop 的,而里面的CFMutableSetRef _rlModes
則表示這個 timer 是在 runLoop 的哪些 mode(__CFRunLoopMode) 中生效钦幔。
同時我們也能看到 __CFRunLoopTimer(即 NSTimer) 使用的是 pthread_mutex_t 鎖。
__CFRunLoopMode
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
從 __CFRunLoopMode 的源代碼中常柄, 我們可看到里面包含著一個CFMutableArrayRef _timers
鲤氢,也就是說 runLoopMode 有一個存放 timer 的數(shù)組,即當(dāng)我們指定一個 timer 的 runLoopMode 的時候西潘,[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]
卷玉, 會把這個 timer 放入相應(yīng)的 runLoopMode 對象中。也就是說這個 runLoopMode 對象里面的 _timers 數(shù)組喷市,會持有這個 timer 對象相种。這一點(diǎn)可以從 Xcode 的 Memory Graph中看到
當(dāng)我們不需要一個 timer 的時候,我們是使用 NSTimer 的一個方法 invalidate
來進(jìn)行解除所有和這個 timer 相關(guān)的持有關(guān)系品姓。
同樣我們也能看到 __CFRunLoopMode 使用的也是 pthread_mutex_t 鎖寝并。
回答前面的問題
當(dāng)我有一個 viewController,持有了一個 timer, 我能不能只調(diào)用 self.timer = nil;
?
不能腹备,因?yàn)?
self.timer = nil;
只是解除了 viewController 持有 timer衬潦,但是并沒有解除 runLoopMode 里面的數(shù)組持有 timer 的關(guān)系,所以必須調(diào)用invalidate來解除所有和 timer 相關(guān)的關(guān)系植酥。