iPad 直播間彈幕優(yōu)化記錄
優(yōu)化彈幕系統(tǒng)
創(chuàng)建過(guò)程優(yōu)化
彈幕系統(tǒng)均蜜,使用的網(wǎng)上開(kāi)源的彈幕控件 https://github.com/unash/BarrageRenderer
這個(gè)彈幕控件的做法是內(nèi)部啟動(dòng)一個(gè)定時(shí)器,不斷的刷新彈幕view 的fame. 彈幕數(shù)量如果非常大的話芒率,就會(huì)一卡一卡的囤耳。
優(yōu)化使用過(guò)程:
加入的彈幕的時(shí)候,調(diào)用如下函數(shù):
- (void)AppendWalkText:(NSString*)str {······}
這里 str 就是彈幕文字偶芍。充择。優(yōu)化做法是創(chuàng)建一個(gè) DISPATCH_QUEUE_SERIAL 的 隊(duì)列,在這個(gè)隊(duì)列里創(chuàng)建好彈幕精靈匪蟀,再扔進(jìn)彈幕系統(tǒng)里椎麦。
代碼大體如下:
[[PTVGCDQueue barrageQueue] execute:^
{
YYTextLayout *layout = [self _layoutWithText:str];
BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init];
descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]);
[descriptor.params safe_setObject:[UIColor clearColor] forKey:@"backgroundColor"];
[descriptor.params safe_setObject:@(1) forKey:@"borderWidth"];
[descriptor.params safe_setObject:[UIColor blackColor] forKey:@"strokeColor"];
······
///TOTO
///#創(chuàng)建好的彈幕精靈扔進(jìn)彈幕系統(tǒng)
[_renderer receive:descriptor]; //< _renderer 已經(jīng)確保了在主線程運(yùn)行
}];
此處 _layoutWithText 函數(shù)的作用是解析字符串,生成 layout 因?yàn)閺椖籿iew使用的YYLabel 所以材彪,提前在后臺(tái)生成 layout,在創(chuàng)建響應(yīng)的彈幕的時(shí)候直接賦值 layout 就行观挎。
隊(duì)列不使用 DISPATCH_QUEUE_CONCURRENT 因?yàn)椋蝿?wù)多的時(shí)候段化,DISPATCH_QUEUE_CONCURRENT 會(huì)創(chuàng)建新的線程嘁捷,導(dǎo)致內(nèi)存暴漲,而且要使用 鎖 來(lái) 保證線程安全显熏。雄嚣。
使用過(guò)程優(yōu)化
增加彈幕的view 的重復(fù)利用。
@interface __BarrageCache : NSObject
//< 單例
+ (__BarrageCache *)instance;
//< 從緩存中取View
- (nullable id)barrageView;
//< 添加view到緩存
- (void)addBarrageView:(nullable UIView *)view;
//< 清空緩存隊(duì)列
- (void)clearViews;
//< 緩存隊(duì)列
@property (nonatomic, strong, readonly) NSMutableSet *set;
@end
做一個(gè)小型的Cache佃延,里面一個(gè) set 來(lái)管理 彈幕view,這樣避免不斷的重復(fù)申請(qǐng)內(nèi)存现诀,釋放內(nèi)存,其實(shí)一般發(fā)熱履肃,大都是因?yàn)椴粩嗟尼尫牛暾?qǐng)內(nèi)存導(dǎo)致的坐桩。
創(chuàng)建丟棄策略
比如屏幕上彈幕超過(guò) 500 條就直接丟棄尺棋,不顯示
優(yōu)化彈幕列表
彈幕列表,就是房間里面的聊天記錄膘螟。彈幕列表,是一個(gè)tableView,所以本質(zhì)上就是優(yōu)化一下 TableView.
跟彈幕的數(shù)據(jù)一個(gè)荆残,等長(zhǎng)連接發(fā)送一個(gè)data 過(guò)來(lái)的時(shí)候奴艾,提前解析成model , 在 后臺(tái)線程(非主線程)内斯!
大概過(guò)程如下:
[[PTVGCDQueue chatQueue] execute:^
{
if (attributedString)
{
//TODO
//解析字符串過(guò)程
//
static PTVTextSimpleEmoticonParser *parser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
parser = [PTVTextSimpleEmoticonParser new];
});
if (!parser.emoticonMapper) {
parser.emoticonMapper = self.dictEmoticon;
}
//< 解析表情
[parser parseText:attributedString selectedRange:NULL];
}
///生成data
ChatMessageData * data = [ChatMessageData new];
[data setMessage:message];
[data setHeight:[message heightForChatRoomMessage:CGRectGetWidth(self.tableView.bounds) string:attributedString]];
///TODO 加入datasource 中,reload
}];
如上俘闯,在tableview reload(當(dāng)然有可能insert 總之就是刷新界面) 之前 提前計(jì)算出 高度,和 layout ,并且存儲(chǔ)在 model 中真朗。 在UITableViewCell setData 的時(shí)候,記得設(shè)置 YYLabel 的frame遮婶。
其他
后臺(tái)釋放添加彈幕過(guò)程中不用的資源。比如 要丟棄的 數(shù)據(jù)旗扑,比如 超過(guò) 500 條 就 刪tableview的數(shù)據(jù)源衍菱,都可以放在后臺(tái)釋放肩豁。
后臺(tái)釋放的代碼脊串,
void ASPerformBlockOnDeallocationQueue(void (^block)())
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.AsyncDisplayKit.deallocationQueue", DISPATCH_QUEUE_SERIAL);
});
dispatch_async(queue, block);
}
這里代碼來(lái)自 ASDisplaykit,使用的時(shí)候,可以這樣子
__block ASTextKitRenderer *renderer = _renderer;
ASPerformBlockOnDeallocationQueue(^{
renderer = nil;
});
_renderer = nil;
此處是在后臺(tái)釋放了 renderer清钥。
設(shè)置UITableViewCell 的背景色 和 tableView 一致琼锋,而不是使用UIClearColor..
if 做到了這一步,還是不夠流暢祟昭,
使用 YYKit 帶的
#import "YYTextTransaction.h"
把任務(wù)分散到 runloop 中缕坎。
用法如下:
[[YYTextTransaction transactionWithTarget:self selector:@selector(_updateIfNeeded)] commit];
優(yōu)化過(guò)程先水這么多,篡悟,一般來(lái)說(shuō)谜叹,使用了 YYLabel ,界面都不會(huì)太卡。