這個文章的前置文章:LNDanmakuMaster
LNDanmakuPool是LNDanmakuMaster針對彈幕場景提出的復(fù)用方案,與UICollectionView/UITableView的復(fù)用邏輯很相似,為任意NSObject類型打標(biāo)簽,并通過標(biāo)簽進(jìn)行NSObject類型的存取。
LNDanmakuPurePool
如果我們想實(shí)現(xiàn)一個可以通過不同標(biāo)簽存取不同對象的池子,首先需要實(shí)現(xiàn)一個只能存取一種固定類型的池子,這個池子就是LNDanmakuPurePool:
@interface LNDanmakuPurePool : NSObject
@property (nonatomic, assign) NSInteger maxCapacity;
@property (nonatomic, assign) Class targetClass;
@property (nonatomic, copy) NSString *poolKey;
- (void)saveObj:(NSObject *)obj;
- (NSObject *)getObj;
- (void)clearPurePool;
@end
這個池子指定了key與Class的對應(yīng)關(guān)系,只有Key類型符合要求時,才可以進(jìn)行存取操作;且存取對象的類型固定為targetClass類型。除此之外,這個池子還定義了最大存儲數(shù)量和清空方法;存儲時,如果當(dāng)前已存儲對象個數(shù)已超過最大容量,則不會繼續(xù)存儲;反之,獲取時,如果當(dāng)前池子已空,則會重新創(chuàng)建對象返回:
- (void)saveObj:(NSObject *)obj
{
if (self.poolMSet.count > self.maxCapacity) {
while (self.poolMSet.count > self.maxCapacity) {
[self.poolMSet removeObject:self.poolMSet.anyObject];
}
} else if (self.poolMSet.count == self.maxCapacity) {
//Fulled .
} else {
if ([obj isKindOfClass:self.targetClass] && [[obj danmakuPoolKey] isEqualToString:self.poolKey]) {
[self.poolMSet addObject:obj];
}
}
}
存儲時會校驗(yàn)obj類型和key是否都合法,obj.danmakuPoolKey是在這個池子創(chuàng)建一個對象時進(jìn)行賦值的,也是“這個對象屬于這個池子”的標(biāo)志屬性(多余的判斷為防止在一些異常情況下也能正常工作):
- (NSObject *)getObj
{
NSObject *resultObj = nil;
while (self.poolMSet.count > 0) {
NSObject *obj = [self.poolMSet anyObject];
if ([obj isKindOfClass:self.targetClass] && [obj.danmakuPoolKey isEqualToString:self.poolKey]) {
resultObj = obj;
[self.poolMSet removeObject:obj];
break;
} else {
[self.poolMSet removeObject:obj];
}
}
if (resultObj) {
return resultObj;
} else {
resultObj = [[self.targetClass alloc] init];
[resultObj setDanmakuPoolKey:self.poolKey];
return resultObj;
}
}
LNDanmakuPool
PurePool已經(jīng)定義好了一種純粹的池子供我們調(diào)用,Pool就可以通過維護(hù)多個PurePool來實(shí)現(xiàn)多種對象的存取了,Pool的方法相當(dāng)簡練:
@interface LNDanmakuPool : NSObject
- (void)registerClass:(Class)class forKey:(NSString *)key;
- (NSObject *)instanceForKey:(NSString *)key;
- (void)saveInstance:(NSObject *)instance;
@end
Pool的內(nèi)部維護(hù)了一個Key和PurePool的映射,注冊過程會為key創(chuàng)建好一個class類型的PurePool,當(dāng)用戶存放或獲取時通過這個映射找到對應(yīng)的PurePool進(jìn)行存取:
@interface LNDanmakuPool ()
@property (nonatomic, strong) NSMutableDictionary <NSString *, LNDanmakuPurePool *> *poolMDic;
@end
存取哪些NSObject?
通常,我們?yōu)槊總€Player配備了一個專門的Pool用來復(fù)用,通常情況下NSAttributes繼承自NSObject,類似于一個包裝袋,它是一次性的且開銷很小,不需要進(jìn)行復(fù)用;customObj是業(yè)務(wù)方規(guī)定的彈幕模型,本身就是不固定的id類型,復(fù)用業(yè)務(wù)模型是不太現(xiàn)實(shí)的;因此,這里強(qiáng)調(diào)的復(fù)用與UICollectionView一樣,是UI層面的復(fù)用:UIView/CAlayer,如果使用方的UIView層級比較復(fù)雜,且有很多布局邏輯、點(diǎn)擊事件等,LNDanmakuMaster推薦從Player的池子中獲取這些UI組件,這樣取出的UI組件都被打上對應(yīng)池子的標(biāo)簽,并會在LNDanmakuMaster的各種時機(jī)自動回收(任意NSAttributes失效之前,這個過程不需要手動處理,只要是register過且從Pool中獲取到的都可以被回收),這些時機(jī)包括:
- Dispatcher從隊(duì)列中丟棄一個Attributes時
- Dispatcher被清理時
- 播放完,已經(jīng)卸載一個Attributes時
- 當(dāng)前Player在非播放中狀態(tài)下,插入Attributes失敗時
Tips
雖然LNDanmakuPlayer提供了Pool來獲取一個UI組件,但并沒有指定必須使用這個UI組件做哪些工作;因此,使用者可以從Pool中獲取一個UI組件,但不把它放在播放器中播放,這樣就從LNDanmakuPlayer的池子里“偷走”了一個UI組件,因?yàn)闆]有經(jīng)過彈幕周期的組件是不會被回收的。