有温度的桃桃胡 發表於 2020-6-25 16:01:00

浅析iOS开发中UITableViewCell的复用机制

<h2 id="写在前面">写在前面</h2>
<p>UITableView是iOS开发中一种非常常用的组件,在主流App中几乎可以看到(微信和QQ的聊天列表等)。这篇文章主要探讨UITableView的数据载体——UITableViewCell的一些相关内容</p>
<h2 id="uitableviewcell是什么">UITableViewCell是什么</h2>
<p>UITableViewCell就是UITableView展示数据的基本单位 可以理解为单元格<br>
此处蓝色背景的为已经填充的Cell 剩下的位置是没有Cell的<br>
<img src="https://img2020.cnblogs.com/blog/1927218/202006/1927218-20200625150644593-549850561.png" alt="" loading="lazy"><br>
实现代码也比较简单</p>
<pre><code>/* 此处代码返回的是UITableViewCell的数量 实际使用中应该与数据源绑定 此处写死 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 10;
}

/* 此处代码返回的是根据indexPath的位置 返回该indexPath应该展示的Cell */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell* cell = [ init];
    cell.textLabel.text = @"UITableViewCell";
    cell.backgroundColor = UIColor.cyanColor;
   
    return cell;
}
</code></pre>
<h2 id="uitableviewcell的复用机制">UITableViewCell的复用机制</h2>
<p>上面的代码这样写,就容易引起一些问题<br>
根据每行都生成一个新的Cell,当行数非常多的时候,将会占用非常大的内存,出于这方面的考虑,Apple引入了UITableViewCell的复用机制,先看这段代码</p>
<pre><code>- (void)setupView {
   
    _tableView = [ initWithFrame:[ bounds]];
    _tableView.delegate = self;
    _tableView.dataSource = self;
   
    /* 要先在UITableView中注册要复用的Cell类型 并设置一个id*/
    forCellReuseIdentifier:@"reuse"];
   
    ;
}

/* 此处代码返回的是UITableViewCell的数量 实际使用中应该与数据源绑定 此处写死 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1000;
}

/* 此处代码返回的是根据indexPath的位置 返回该indexPath应该展示的Cell */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    /* 此处从UITableView中的复用队列中取cell 使用的id要与注册时使用的id相同*/
    UITableViewCell* cell = ;
   
    printf("cell---------%ld被添加到视图中了\n", (long)indexPath.row);
    NSLog(@"%@", cell.description);
   
    cell.textLabel.text = @"UITableViewCell";
    cell.backgroundColor = UIColor.cyanColor;
   
    return cell;
}
</code></pre>
<p>此处是复用机制的实现 特意使cell放到了1000个,下面看打印结果<br>
<img src="https://img2020.cnblogs.com/blog/1927218/202006/1927218-20200625155436586-627177923.png" alt="" loading="lazy"><br>
最终打印了20个 即UITableView在启用复用机制后 会事先载入两倍于屏幕的cell数量<br>
<img src="https://img2020.cnblogs.com/blog/1927218/202006/1927218-20200625155454136-1681421025.png" alt="" loading="lazy"><br>
下面我们向下拉动屏幕 会发现如下打印结果:<br>
<img src="https://img2020.cnblogs.com/blog/1927218/202006/1927218-20200625155535779-1580369751.png" alt="" loading="lazy"><br>
其实会发现和这些cell的地址与cell0-4的地址是相同的,证明发生了复用</p>
<p>如果不使用复用机制会是下面的结果<br>
<img src="https://img2020.cnblogs.com/blog/1927218/202006/1927218-20200625155727032-1556463488.png" alt="" loading="lazy"><br>
可以发现地址变了 不再是复用</p>
<p>总结一下<br>
在UITableView的滑动过程中如果启用了复用机制,会将滑出屏幕的cell出队 等待下一次的使用,队列中的cell数量一定,通过来回反复出队入队实现复用</p>
<h2 id="uitableviewcell的其他的一些简单的性能优化问题">UITableViewCell的其他的一些简单的性能优化问题</h2>
<h3 id="离屏渲染off-screen-rendering">离屏渲染(Off-Screen Rendering)</h3>
<p>(其实这一块我也是一知半解,还需要不断的加强学习)<br>
代码被装入内存执行过程中,在CPU中完成对视图布局的计算(此处应包含对AutoLayout的计算),布局属性计算完成后,提交到GPU渲染,渲染成图形再提交到显示器进行展示</p>
<p>离屏渲染就是由于用户做了一些对视图图层的操作,如圆角、阴影等,这些操作会触发离屏渲染,即由GPU再开一个“屏幕”进行渲染,渲染结束后提交到显示器,即不在当前显示器做渲染操作,这样是相对比较影响流畅性的</p>
<p>根据上面的分析我们可以得知,UITableView在不断滑动中会不断的有Cell滑入滑出,这样的话对CPU计算布局和GPU渲染的开销相对较大,如果再触发离屏渲染,就可能会影响帧率,影响用户体验。</p>
<p>至于怎么高效的避免离屏渲染和割圆角,网上的大牛文章很多,本文暂不说明(我也不太会用)</p>
<h3 id="高度适配问题">高度适配问题</h3>
<p>众所周知实际使用中Cell中数据多种多样,比如一个简单的Label可能实际内容量会大于一行,这样就需要AutoLayout来将这个Cell“撑开”以使他能展示全部的内容<br>
但是又众所周知,AutoLayout会比较影响性能,毕竟是动态计算,我们可以采取如下方法优化该问题:</p>
<p>首先利用NSAttributedString通过代码方式计算出文本的高度 文本为calcedStr<br>
此处引用了iOS计算文本高度的几种方法</p>
<pre><code> NSMutableParagraphStyle *paragraphStyle = [ mutableCopy];
    paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
    paragraphStyle.alignment = NSTextAlignmentLeft;
    paragraphStyle.lineSpacing = 2;

NSDictionary* attributes = @{NSFontAttributeName:,
                                 NSParagraphStyleAttributeName: paragraphStyle,
                                 NSForegroundColorAttributeName: };
   
CGRect rect = [calcedStr boundingRectWithSize:limitSize
                                     options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                  attributes:attributes
                                     context:nil];
</code></pre>
<p>然后再UITableView的代理方法中实现<br>
然后再根据数据源文本,计算高度缓存到一个数组中,再在此处返回数组indexPath对应值即可</p>
<pre><code>/* 此处代码返回的是根据indexPath的位置 返回该处cell的高度*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 100.0;
}
</code></pre><br><br>
来源:https://www.cnblogs.com/vegetablefriend/p/13191740.html
頁: [1]
查看完整版本: 浅析iOS开发中UITableViewCell的复用机制