美文网首页
性能优化

性能优化

作者: Abner_XuanYuan | 来源:发表于2023-03-07 00:01 被阅读0次

1、启动相关优化

1、Main 函数执行前

1、加载可执行文件(mach-o文件)。
2、加载动态链接库,进行 rebase 指针调整和 bind 符号绑定。
3、Objc 运行时的初始化处理,包括 Objc 相关类的注册、category 注册、selector 唯一性检查
4、初始化,包括执行了+load() 方法、attribute((constructor)) 修饰的函数调用、创建 C++ 静态全局变量。

优化方案
1、减少动态库加载。动态库较多时,尽量将多个动态库进行合并。
2、减少非启动所需类和方法,或延迟加载。
3、尽量减少 +load() 方法使用,尽量用 +initialize() 方法替换 +load()方法。
4、尽量减少 C++ 全局变量的数量。

2、Main函数执行后

从 main() 函数执行到 Appdelegate 的 didFinishLaunchingWithOptions 方法执行完成。
1、首屏初始化所需要配置文件的读写操作。
2、首屏列表大数据的读取。
3、首屏渲染的大量计算。

优化方案
尽量减少首屏加载内容,非首屏数据不加载,非首屏必要数据不加载,首屏必要数据按重要性分批次加载。

3、启动监控方案

1、定时抓取主线程上的方法调用堆栈,计算一段时间里各个方法的耗时。
2、对 objc_msgSend 方法进行 hook 来掌握所有方法的执行耗时。

2、卡顿

1、CPU&GPU

CPU 任务:对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)等。
GPU 任务:渲染。

优化方案
1、尽量减少 CPU、GPU 任务。
2、尽量用轻量级的对象。
3、尽量减少属性的调用次数和修改。
4、尽量提前计算好布局。
5、Autolayout 会比直接设置 frame 消耗更多的 CPU 资源。
6、UIImageView 大小尽量和图片大小一致。
7、尽量减少线程的最大并发数量。
8、有大量图片时做到按需加载。
9、GPU 能处理的最大纹理尺寸是 4096x4096,一旦超过这个尺寸,就会占用 CPU 资源进行处理,所以纹理尽量不要超过这个尺寸。
10、尽量减少视图数量和层次。
11、减少透明的视图(alpha<1),不透明的就设置 opaque 为YES。
12、尽量把耗时的操作放到子线程(文本尺寸、图片处理)。

2、UITableView 优化

1、善用重用标识。
2、设置预估行高,预先缓存动态行高。
对一些 cell 高度不固定的 Cell 要做一下缓存 cell 高度。获取到数据后子线程计算高度并写到 model 的 cellHeight 属性中,使用时读取 cellHeight 。
3、减少 SubViews 层级、异步绘制、避免离屏渲染、使用 Hidden 隐藏图层。
4、分屏加载数据,预先异步请求数据。

// 先请求网络数据来获取一些初始化数据,然后再利用 UITableView 的 Prefetching API 来对数据进行预加载,从而来实现数据的无缝加载。

UITableViewDataSourcePrefetching 协议
// this protocol can provide information about cells before they are displayed on screen.
@protocol UITableViewDataSourcePrefetching <NSObject>
@required
//基于当前滚动的方向和速度对接下来的 IndexPaths 进行 Prefetch,通常我们会在这里实现预加载数据的逻辑
// indexPaths are ordered ascending by geometric distance from the table view
- (void)tableView:(UITableView *)tableView prefetchRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;

@optional
//当用户快速滚动导致一些 Cell 不可见的时候,你可以通过这个方法来取消任何挂起的数据加载操作,有利于提高滚动性能
// indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -tableView:prefetchRowsAtIndexPaths:
- (void)tableView:(UITableView *)tableView cancelPrefetchingForRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
@end

//使用
extension ViewController: UITableViewDataSourcePrefetching {
    // 翻页请求
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        let needFetch = indexPaths.contains { $0.row >= viewModel.currentCount}
        if needFetch {
            // 1.满足条件进行翻页请求
            indicatorView.startAnimating()
            viewModel.fetchImages()
        }
        
        for indexPath in indexPaths {
            if let _ = viewModel.loadingOperations[indexPath] {
                return
            }
            
            if let dataloader = viewModel.loadImage(at: indexPath.row) {
                print("在 \(indexPath.row) 行 对图片进行 prefetch ")
                // 2 对需要下载的图片进行预热
                viewModel.loadingQueue.addOperation(dataloader)
                // 3 将该下载线程加入到记录数组中以便根据索引查找
                viewModel.loadingOperations[indexPath] = dataloader
            }
        }
    }

    
    func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]){
        // 该行在不需要显示的时候,取消 prefetch ,避免造成资源浪费
        indexPaths.forEach {
            if let dataLoader = viewModel.loadingOperations[$0] {
                print("在 \($0.row) 行 cancelPrefetchingForRowsAt ")
                dataLoader.cancel()
                viewModel.loadingOperations.removeValue(forKey: $0)
            }
        }
    }
}

//补充额外两个方法
    // 用于计算 tableview 加载新数据时需要 reload 的 cell
    func visibleIndexPathsToReload(intersecting indexPaths: [IndexPath]) -> [IndexPath] {
        let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows ?? []
        let indexPathsIntersection = Set(indexPathsForVisibleRows).intersection(indexPaths)
        return Array(indexPathsIntersection)
    }
    
    // 用于确定该索引的行是否超出了目前收到数据的最大数量
    func isLoadingCell(for indexPath: IndexPath) -> Bool {
        return indexPath.row >= (viewModel.currentCount)
    }

5、滑动 TableView 时,按需加载内容。

#pragma mark - UIScrollViewDelegate  
//按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。  
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {  

    NSIndexPath *targetPath = [_myTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];  
    NSIndexPath *firstVisiblePath = [[_myTableView indexPathsForVisibleRows] firstObject];  
    NSInteger skipCount = 8;  
    if (labs(firstVisiblePath.row - targetPath.row)>  skipCount) {  
        NSArray *temp = [_myTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _myTableView.frame.size.width, _myTableView.frame.size.height)];  
        NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];  
        if (velocity.y<0) {  
            NSIndexPath *indexPath = [temp lastObject];  
            if (indexPath.row+33) {  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];  
            }  
        }  
        [_dataList addObjectsFromArray:arr];  
    }  
}  

// targetContentOffset 是 TableView 减速到停止的地方, velocity 表示速度向量。

6、cell 类中应该避免请求网络加载数据。
7、在 willDisplayCell:forRowAtIndexPath: 代理方法中的绑定数据。
在 tableView: cellForRowAtIndexPath: 方法中获取/创建 cell ,在 willDisplayCell:forRowAtIndexPath: 绑定数据,这个方法在显示cell之前会被调用。
8、尽量避免离屏渲染。
9、局部刷新。

3、耗电优化

1、尽可能降低 CPU/GPU 功耗。
2、少用定时器。
3、优化I/O操作。
4、尽量不要频繁写入小数据,最好批量一次性写入。
5、读写大量重要数据时,考虑用 dispatch_io,其提供了基于 GCD 的异步操作文件I/O的API。用 dispatch_io 系统会优化磁盘访问。
6、数据量比较大的,建议使用数据库(比如SQLite、CoreData)。

4、网络优化

1、减少、压缩网络数据。
2、如果多次请求的结果是相同的,尽量使用缓存。
3、使用断点续传,否则网络不稳定时可能多次传输相同的内容。
4、网络不可用时,不要尝试执行网络请求。
5、让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。
6、批量传输、下载,避免多次调用接口。

5、定位优化

1、如果只是需要快速确定用户位置,最好用 CLLocationManager的requestLocation 方法。定位完成后,会自动让定位硬件断电。
2、如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务。
3、尽量降低定位精度,比如尽量不要使用精度最高的 kCLLocationAccuracyBest。
4、需要后台定位时,尽量设置 pausesLocationUpdatesAutomatically 为 YES,如果用户不太可能移动的时候系统会自动暂停位置更新。
5、尽量不要使用 startMonitoringSignificantLocationChanges,优先考虑 startMonitoringForRegion: 。
6、用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件。

相关文章

  • Android性能优化 - 消除卡顿

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化 - 内存优化 性能分析工具 - Tra...

  • Android性能优化 - 内存优化

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化- 内存优化 性能分析工具 - Trac...

  • 前端性能优化(中)

    性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 《前端性能优化(上)...

  • 前端性能优化(下)

    性能优化调研系列文章 《前端性能优化(上)》 《前端性能优化(中)》 《前端性能优化(下)》 《前端性能优化(中)...

  • Awesome Extra

    性能优化 性能优化模式 常见性能优化策略的总结 Spark 性能优化指南——基础篇 Spark 性能优化指南——高...

  • 常用的后端性能优化六种方式:缓存化+服务化+异步化等

    性能优化专题 前端性能优化 数据库性能优化 jvm和多线程优化 架构层面优化 缓存性能优化 常用的后端性能优化六大...

  • webpack 性能优化

    webpack性能优化 开发环境性能优化 生产环境性能优化 开发环境性能优化 优化打包构建速度 优化调试功能 生产...

  • iOS性能优化 - 整理

    本文主要包含: 性能优化 - 卡顿性能优化 - 耗电优化性能优化 - APP启动优化安装包瘦身 一  性能优化 -...

  • 【React.js 20】React性能优化

    React性能优化 React性能优化主要分三块: React 组件性能优化 属性传递优化针对单组件性能优化,很多...

  • Android性能优化(下)

    Android性能优化 内存泄漏和性能优化方式Android性能优化(上)数据库优化和网络优化Android性能优...

网友评论

      本文标题:性能优化

      本文链接:https://www.haomeiwen.com/subject/keevldtx.html