ios之富文本在tableviewcell去加载html字符串的优化方案
1.相信用iOS系统的类去加载html字符串很多人第一反应就是
NSString *contens = @"1231我给你数<a herf="www.baidu.com">点这里</a>"; NSData *data = [contens dataUsingEncoding:NSUnicodeStringEncoding]; NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil];
然后后面的就是直接将attr这个对象赋值给lab或者textView这样的控件就能展示了,苹果提供的API就是这个。
然而我要说的是在数据量大的情况下intiWithData的时候它是比较耗费内存性能的,你可以尝试的去把他放在tableview里面的cell去加载,你会发现滚动起来后通过cell复用机制去加载会使UI界面变得卡顿,那么为什么会造成这种原因呢?通过反复的去验证,我发现反复的intiWithData确实挺爆内存的,后来就找原因。
网上也看过别人写的一个优化的方案,很直接的就是你可以异步开辟子线程去加载这个attr然后在主线程去赋值,这样就可以了。
// 获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_async(queue, ^{ contents = [contents stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"]; NSData *data = [content dataUsingEncoding:NSUnicodeStringEncoding]; NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil]; [attr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, attr.length)]; //回主线程刷新ui dispatch_async(mainQueue, ^{ //给UI控件赋值 self.lab.attr = attr; }); });
类似于这种,可以减少内存使用率让UI不卡顿。但是这种情况下你要考虑是否适合当前的场景,例如你创建的这个cell是根据富文本里面的高度去计算的,那么你就得仔细考虑一下了,上面这段代码是通过异步进行加载的,那么的话你要计算出高度的话就得异步去拿高度,但是当异步拿到高度的时候你cell很有可能已经创建完了,时机没办法同步。而且当你高度拿到后再进行reloadData刷新的话,那么整个tableview是会重新布局的,那么又会去重新计算,这样会出现闪屏的现象,那么废话说了那么,原因就是因为它加载的时候转换的contens内容不多或者只initWithData一两次还好,但是cell的滚动复用会让他多次加载,因此不能放在cell创建的时候去执行。
下面是我个人的思路,有其他想法的可以提出来一起交流:
1.如果cell里面加载会反复执行这段代码的话,就会消耗内存及卡顿UI,那么我在cell创建前提前做好这个事情,让他不需要initWithData多次。
在模型的.h里面创建attr属性
@interface TestModel : NSObject @property (strong,nonatomic) NSMutableAttributedString *attr; @end
在.m里去做刚才的这一步,那么就是说当请求数据结束后再进行富文本的转换在赋值给模型保存,而不是创建cell的时候去加载富文本
+ (instancetype)initModelWithDict:(NSDictionary *)dict { //初始化 id obj = [[self alloc] init]; //字典转模型 [obj setValuesForKeysWithDictionary:dict]; //转富文本 if (contens) { NSData *data = [contens dataUsingEncoding:NSUnicodeStringEncoding]; NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil]; [attr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, attr.length)]; model.attr = attr; } return obj; }
那么,模型走完这一步再去刷新reloadData 那么cell加载的时候就可以直接将model里的attr给cell里面的lab或者textView控件的attr就行了,每次就是从模型里面去取值,这样性能就会好点。
后面要计算高度可以冲模型里面取到attr对象,然后根据lab或者textView调用系统的计算布局就行,下面的仅供参考
CGFloat labH = [self.lab boundingRectWithSize:CGSizeMake(375, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil].size.height;