cell不复用导致内存暴涨的问题: 内存从100多MB到700多MB

封面图

记得上一次装逼过头地说,cell我不复用照样能运行,不是复用么,我偏不复用,每次都创建新的,结果导致的问题可想而知,项目里的item数目少还好说,一旦超过一定数量就难以支撑了,甚至手机发烫直接闪退......当然那只是一时不服气的说道,小飞哥知道我这个坎没过去,他说迟早还会碰壁的,让我自己深刻一次,我想就是这一次吧. 还好这个问题我自己及时发现并逐步解决。想想都有点刺激,虽然再也碰不到第二次了。 下面是记录我这一次修复bug的心路历程。

iOS开发中最最常用到的功能良好,API封装优秀的两大组件,分别是UITableViewUICollectionView,它们都有一个共同的特性,也可以说是优点,那就是cell的复用机制。

  • cell的复用机制

无论是UITableView还是UICollectionView,它们都是属于列表的形式而存在,由于手机屏幕有限,很多时候需要滚动来实现阅读到更多的内容,这和设计理念也有关,大量规则有序的重复的样式或者图片文字的摆放,我们可以创建一个类似于模板的东西那就是cell,cell的复用就是使用模板的过程,比如说一个列表里有很多种样式,那就是很多种cell,每种样式匹配不同的cell模板。

一般来说一个屏幕只能容纳有限个cell,假如数据源里有很多同一种模型数据,需要得到展示,也就是说刚一开始的时候看到的那几个cell,在滚动过程中(假如是从下往上滚,当然从上往下滚也是同样的道理)在屏幕顶部的一个cell完全滚出屏幕外以及屏幕底部滚出来一个cell到屏幕里,就是这个过程发生的事情。cell有一个缓存池类似于一个容器,将不需要用到的空闲cell装进这个容器,即滚粗屏幕外的cell;需要用到类似模板的cell的时候,从这个cell的缓存池容器里取,即从底部滚到屏幕里的cell




cell复用的错误示范,如图重影

这重影贼特么扎心

❌反面教程

优化前
/** 一段是一个类似于微信朋友圈或者微博的列表cell,这里是存在问题的 */
- (UNTopicListTableViewCell *)createHavePhotosCellWithTopicFrameModel:(UNTopicListCellFrame *)topicFrame  tableView:(UITableView *)tableView{
      
    UNTopicListTableViewCell *cell = nil; ////这一句和”!cell”是等效的,所以,这样每次都去创建cell,导致内存疯长,一度达到了738MB之多
        /// TODO !!!  图片一样多才复用好嘛
    if (self.photos.count == topicFrame.topicModel.picurls.count) {
        cell = [tableView dequeueReusableCellWithIdentifier:[UNTopicListTableViewCell resuseidentifyingCellID]];
    }

    if (!cell) {
        [self.photos removeAllObjects];
        cell = [[UNTopicListTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:[UNTopicListTableViewCell resuseidentifyingCellID]];
    }
        // 给cell传递模型数据
    cell.topicFrame  =  topicFrame;
        ///创建图片浏览
    [self createPhotoBrowserWithTopicModel: topicFrame.topicModel topicCell: cell];
    return cell;
}

进一步优化---~

优化后
/** 先去缓存池里去取,取不到就去创建,创建需要判断,如果有一样的模板cell,就可以复用,不要重复去创建,图片数量不一样多cell长得不一样,那就重新创建一个,大致逻辑就这样 */
- (UNTopicListTableViewCell *)createHavePhotosCellWithTopicFrameModel:(UNTopicListCellFrame *)topicFrame  tableView:(UITableView *)tableView{
    UNTopicListTableViewCell *  cell = [tableView dequeueReusableCellWithIdentifier:[UNTopicListTableViewCell resuseidentifyingCellID]];
        
    if (self.photos.count != topicFrame.topicModel.picurls.count) {
        if (!cell) {
            [self.photos removeAllObjects];
            cell = [[UNTopicListTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:[UNTopicListTableViewCell resuseidentifyingCellID]];
        }
    }
        // 给cell传递模型数据
    cell.topicFrame  =  topicFrame;
        ///创建图片浏览
    [self createPhotoBrowserWithTopicModel: topicFrame.topicModel topicCell: cell];
    return cell;
}

再次优化

基于前两步只是解决了内存暴涨的问题,但是复用问题没有解决,看了网红大牛的分享,都说好文章都是改出来的,因为前面的经验缺乏,很多的细节部分考虑不周,这样暴露出来的问题也就越多,有问题就得解决嘛,所以一步一步来,你想要的时间都会给你...

- (UNClubCommentListTableViewCell *)createHavePhotosCellWithEvaluteFrameModel:(UNCommentFrame *)evaluateframe  tableView:(UITableView *)tableView{
  /** 这一点很关键,之前帖子的是固定最多三张图,所以没换行,
     没察觉出来,这个出现cell图片重影现象,
    一下子让我定位出来是复用的bug,复用的cell不正确,
    一定是celIID没搞好,所以有多少张图片就以图片张数作为cellID的依据,这样很like*/

 NSString *photoCellID = [NSString stringWithFormat:@"the%ld_CellID",evaluateframe.model.images.count];
    UNClubCommentListTableViewCell *cell =  [tableView dequeueReusableCellWithIdentifier:  photoCellID];
    
    if (self.photos.count != evaluateframe.model.images.count) {
        [self.photos removeAllObjects];
        if (!cell) {
            cell = [[UNClubCommentListTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: photoCellID];
            cell.separatorInset = UIEdgeInsetsMake(0, 15, 0, 15);
        }
    }
        // 给cell传递模型数据
    cell.commentFrame  =  evaluateframe;
        ///创建图片浏览
    [self createPhotoBrowserWithEvaluteModel: evaluateframe.model evaluteCell: cell];
    return cell;
}

![😓]再次优化cell图片错乱的问题 (一次次打脸,再也不写什么正确姿势了,每一次的更新就是在纠正自己)

优化列表图片与图片浏览器看到的图片不一致,cell复用成功了,cell图片个数和ID保持数据源图片张数一致,但是不明白为啥还是出现了重复的图片,有残留?我也不确定是不是SDwebImage的缓存,之后网上查了一圈,发现没有啥特别好的解决方案. 只能是把子视图移除重新创建一遍,但是整一个cell还是复用的,局部不复用

列表上看到也是单张图,是这样的
点击图片浏览器是这样的,问题是数据源是同一个链接

于是,有了局部不复用的操作:
在cell里不需要在和图片浏览器的数组数据源保持一致了,因为一开始就想错了.图片浏览器数据源是在点击cell的某一张图时加进去的,而cell是网络请求获取到数据源时进行展示的,动作与时机不同,不能混为一谈。

- (UNClubCommentListTableViewCell *)createHavePhotosCellWithEvaluteFrameModel:(UNCommentFrame *)evaluateframe  tableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath{
    UNCommentFrame *commentFrame = self.dataSource[indexPath.row];
    static NSString *photoCellID = @"photoCellID";
    UNClubCommentListTableViewCell *cell =  [tableView dequeueReusableCellWithIdentifier: photoCellID];
        if (!cell) {
            cell = [[UNClubCommentListTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: photoCellID];
            cell.separatorInset = UIEdgeInsetsMake(0, 15, 0, 15);
        }
        cell.commentFrame = commentFrame;
        ///创建图片浏览
    [self createPhotoBrowserWithEvaluteModel: commentFrame.model evaluteCell: cell];
    return cell;
}

移除子视图上的图片,每次赋值重新创建

- (void)setPhotos:(NSArray *)photos{
    _photos = photos;
    [self removeAllSubviews];

    NSUInteger photosCount = photos.count;
        // 创建足够数量的图片控件
        // 这里的self.subviews.count不要单独赋值给其他变量
    while (self.subviews.count < photosCount) {
        NSInteger index = self.subviews.count;
        UIImageView *photoView = [[UIImageView alloc] init];
        photoView.contentMode = UIViewContentModeScaleToFill;
        photoView.userInteractionEnabled = YES;
        Weak(weakSelf);
        [photoView addTapActionWithBlock:^(UIGestureRecognizer *gestureRecoginzer) {
            BLOCK_EXEC(weakSelf.clickCommentImgPage,index);
        }];
        photoView.image = nil;
        [photoView sd_setImageWithURL:[NSURL URLWithString: [photos[index] jj_200X200_Image]] placeholderImage:[UIImage imageNamed:@"suprise"] options:SDWebImageRefreshCached];
        [self addSubview:photoView];
    }
}
总结: 爬坑的过程虽然是异常艰辛的,为了不重复沦陷,就是再简单的东西还是要记录一下自己的心得,下次别人面试问我1加1等多少的时候,我就可以确切地告诉他等于2。
图虽丑,意思到位足矣

参考博客:

iOS之TableViewCell重用机制避免重复显示问题
ios开发之解决重用TableViewCell导致的界面错乱的问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容