面试题答案
一键面试重用机制
- 实现思路:UITableView 提供了单元格重用机制,通过复用已移出屏幕的单元格,避免频繁创建新单元格,从而节省内存和提高性能。
- 代码实现要点:
- 在
UITableViewDataSource
协议的tableView:cellForRowAtIndexPath:
方法中使用dequeueReusableCellWithIdentifier:
方法来获取可复用的单元格。 - 为每个单元格类型设置唯一的标识符(identifier)。例如:
- 在
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// 配置单元格内容
cell.textLabel.text = [self.dataArray objectAtIndex:indexPath.row];
return cell;
}
异步加载图片
- 实现思路:避免在主线程中加载图片,因为图片加载可能是耗时操作,会阻塞主线程,导致界面卡顿。使用异步加载的方式,在后台线程加载图片,加载完成后回到主线程更新 UI。
- 代码实现要点:
- 可以使用
NSURLSession
进行网络图片加载,配合NSOperationQueue
实现异步加载。例如,使用AFNetworking
框架(较常用):
- 可以使用
#import <AFNetworking.h>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// 获取图片 URL
NSURL *imageURL = [NSURL URLWithString:self.imageURLs[indexPath.row]];
// 使用 AFNetworking 加载图片
[[SDWebImageManager sharedManager] loadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
// 确保是当前 indexPath 的 cell
if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
cell.imageView.image = image;
}
});
}
}];
return cell;
}
- 若不使用第三方框架,使用
NSURLSession
原生实现:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSURL *imageURL = [NSURL URLWithString:self.imageURLs[indexPath.row]];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:imageURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data &&!error) {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
cell.imageView.image = image;
}
});
}
}];
[task resume];
return cell;
}
减少视图渲染
- 实现思路:尽量减少单元格内视图的数量和复杂度,避免复杂的视图层级和透明视图,因为这些都会增加渲染的开销。
- 代码实现要点:
- 例如,若单元格只需要简单的文本和图片展示,不要添加多余的装饰视图。如果必须有复杂视图,考虑将其绘制到
UIImage
上,然后作为一个简单的UIImageView
展示在单元格中。例如:
- 例如,若单元格只需要简单的文本和图片展示,不要添加多余的装饰视图。如果必须有复杂视图,考虑将其绘制到
// 假设要绘制一个带渐变背景和文本的复杂视图
- (UIImage *)createComplexImage {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 50), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制渐变背景
CGGradientRef gradient;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {1.0, 0.0, 0.0, 1.0, // 起始颜色(红色)
0.0, 1.0, 0.0, 1.0}; // 结束颜色(绿色)
CGFloat locations[] = {0.0, 1.0};
gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(100, 50), 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
// 绘制文本
NSString *text = @"Complex View";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
[text drawAtPoint:CGPointMake(10, 10) withAttributes:attributes];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
然后在单元格中使用这个 UIImage
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.imageView.image = [self createComplexImage];
return cell;
}
预计算高度
- 实现思路:如果单元格高度是动态的,提前计算好每个单元格的高度并缓存起来,避免在
tableView:heightForRowAtIndexPath:
方法中每次都进行计算,减少开销。 - 代码实现要点:
- 在数据源加载完成后,遍历数据并计算每个单元格高度,存储在一个数组中。例如:
- (void)loadData {
// 假设从网络获取数据
NSArray *newData = [self fetchDataFromNetwork];
self.dataArray = newData;
// 预计算高度
NSMutableArray *heightArray = [NSMutableArray arrayWithCapacity:self.dataArray.count];
for (int i = 0; i < self.dataArray.count; i++) {
// 根据数据计算高度,这里假设简单计算
CGFloat height = [self calculateHeightForData:self.dataArray[i]];
[heightArray addObject:@(height)];
}
self.heightCacheArray = heightArray;
}
- (CGFloat)calculateHeightForData:(id)data {
// 实际根据数据内容计算高度,例如文本高度等
NSString *text = data;
CGSize size = [text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]}];
return size.height + 20;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.heightCacheArray[indexPath.row] floatValue];
}
按需加载数据
- 实现思路:不要一次性加载所有数据,而是根据用户的滚动情况,当用户接近表格底部时,加载更多数据,这样可以减少内存占用和首次加载时间。
- 代码实现要点:
- 使用
UITableViewDelegate
的scrollViewDidScroll:
方法监听滚动事件,判断是否接近底部。例如:
- 使用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
CGFloat scrollViewHeight = scrollView.frame.size.height;
if (offsetY > (contentHeight - scrollViewHeight - 100)) {
// 接近底部,加载更多数据
[self loadMoreData];
}
}
- (void)loadMoreData {
// 从网络加载更多数据
NSArray *newData = [self fetchMoreDataFromNetwork];
// 将新数据添加到数据源数组
[self.dataArray addObjectsFromArray:newData];
// 刷新表格
[self.tableView reloadData];
}