問題
在很多場景下可能需要在cell或view中添加計時器進行倒計時的處理,但是這里會有一個問題就是timer的釋放問題允坚。如果沒有在controller釋放時進行釋放像寒,timer會一直被強引用造成內(nèi)存泄露。
在網(wǎng)上看到了幾種解決方案,但是都不是很理想汁蝶,最終在這篇文章中找到了覺得是最好的方法。但是在這個文章里用到了RAC的開源庫论悴,對于大部分人來說可能并不會引用這個庫掖棉。在這里會通過類別的方式來對最后一種方案來進行改造,使得可以在任何地方使用膀估。
方案分析
在這里簡單說明下解決方案幔亥。由于在cell中使用了NSTimer
作為屬性,在這種情況下cell不會自動調(diào)用-dealloc
方法來釋放內(nèi)存察纯。這樣的話我們只能通過在controller釋放內(nèi)存時來釋放timer來達到目的帕棉。在這里我們會添加兩個類別针肥,一個是UIView+ManagerCaller.h
類別來獲取持有view的controller實例,一個是NSObject+DeallocBlock.h
類別來添加調(diào)用-dealloc
函數(shù)時的回調(diào)函數(shù)香伴。
貼下代碼
#import "UIView+ManagerCaller.h"
@implementation UIView (ManagerCaller)
- (UIViewController*)getManagerController
{
UIResponder* responder = self;
while (responder && ![responder isKindOfClass:[UIViewController class]]) {
responder = responder.nextResponder;
}
if (!responder) {
return nil;
}
if ([responder isKindOfClass:[UIViewController class]]) {
return (UIViewController*)responder;
}
return nil;
}
@end
#import "NSObject+DeallocBlock.h"
#import <objc/runtime.h>
@interface FDOnDeallocBlockHolder : NSObject
@property (copy, nonatomic) FDObjectDeallocBlock block;
@property (assign, nonatomic) void* key;
@end
@implementation FDOnDeallocBlockHolder
- (instancetype)initWithBlock: (FDObjectDeallocBlock)block
{
self = [super init];
self.block = block;
return self;
}
- (void)dealloc
{
FDObjectDeallocBlock block = _block;
_block = nil;
if (block) {
block();
}
}
@end
@implementation NSObject (DeallocBlock)
- (void*)addOnDeallocBlock: (FDObjectDeallocBlock)block forKey: (void*)key
{
FDOnDeallocBlockHolder* holder = [[FDOnDeallocBlockHolder alloc] initWithBlock:block];
if (!key) {
key = ((__bridge void*)holder);
} else {
holder.key = key;
}
objc_setAssociatedObject(self, key, holder, OBJC_ASSOCIATION_RETAIN);
[self cacheOnDeallocBlockHolder:holder];
return key;
}
- (void*)addOnDeallocBlock: (FDObjectDeallocBlock)block
{
return [self addOnDeallocBlock:block forKey:nil];
}
- (BOOL)hasOnDeallocBlockWithKey: (void*)key
{
return objc_getAssociatedObject(self, key) != nil;
}
- (void)removeOnDeallocBlockByKey: (void*)key
{
NSMutableArray* list = [self onDeallocBlockHolderList];
for (NSValue* holderReference in list) {
FDOnDeallocBlockHolder* holder = holderReference.nonretainedObjectValue;
if (holder && (holder == key || holder.key == key)) {
holder.block = nil;
[list removeObject:holderReference];
break;
}
}
objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN);
}
- (void)cacheOnDeallocBlockHolder: (FDOnDeallocBlockHolder*)holder
{
NSMutableArray* list = [self onDeallocBlockHolderList];
[list addObject:[NSValue valueWithNonretainedObject:holder]];
}
- (NSMutableArray<NSValue*>*)onDeallocBlockHolderList
{
NSMutableArray* list = objc_getAssociatedObject(self, @selector(onDeallocBlockHolderList));
if (!list) {
list = [[NSMutableArray alloc] init];
objc_setAssociatedObject(self, @selector(onDeallocBlockHolderList), list, OBJC_ASSOCIATION_RETAIN);
}
return list;
}
- (void)executeAllOnDeallocBlocks
{
NSMutableArray* list = [self onDeallocBlockHolderList];
for (NSValue* holderReference in list) {
FDOnDeallocBlockHolder* holder = holderReference.nonretainedObjectValue;
if (!holder) {
continue;
}
FDObjectDeallocBlock block = holder.block;
holder.block = nil;
if (!block) {
continue;
}
void* key = ((__bridge void*)holder);
objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN);
block();
}
[list removeAllObjects];
}
@end
代碼分析
UIView+ManagerCaller.h
這個類別的邏輯就是通過遍歷View的所有Responder來找到持有View的controller慰枕。NSObject+DeallocBlock.h
在OC中釋放內(nèi)存邏輯是先釋放實例自身再釋放實例成員變量。在這里我們通過這個特性來給實例添加一個新成員變量來實現(xiàn)回調(diào)即纲,把回調(diào)函數(shù)賦值給新成員變量具帮,在實例自身釋放后新成員變量釋放時調(diào)用-dealloc
方法時執(zhí)行回調(diào)來達到目的。添加回調(diào)時機
在這里會有一個添加回調(diào)時機的問題低斋,因為由于加載cell方式不同會略有不同蜂厅。如果通過代碼加載cell的話,在執(zhí)行-didMoveToSuperview
函數(shù)時就可以獲取到controller實例膊畴。但是如果通過XIB加載cell的情況下葛峻,在調(diào)用這個函數(shù)時是獲取不到controller實例的。為了保險起見巴比,可以統(tǒng)一放到-didMoveToWindow
函數(shù)里來添加回調(diào)函數(shù)。這里也要注意需要做好判斷邏輯不要重復(fù)添加回調(diào)函數(shù)礁遵,因為-didMoveToWindow
函數(shù)可能會在不同情況下觸發(fā)多次轻绞。
效果演示
下面貼兩張圖,分別是添加回調(diào)函數(shù)前后Log對比佣耐。
總結(jié)
在上面這個問題里政勃,主要解決思路就是怎么讓cell或者subview在controller觸發(fā)-dealloc
后執(zhí)行回調(diào)函數(shù),這里涉及到了內(nèi)存釋放機制兼砖,我在這里也只是簡單應(yīng)用了而已奸远,可能還有一些情況沒有考慮到會有其他問題。后續(xù)還是要再研究下內(nèi)存釋放機制的更深層的內(nèi)容讽挟。