问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

一文掌握iOS应用性能优化:内存、启动、渲染全方位解析

创作时间:
2025-01-22 01:18:23
作者:
@小白创作中心

一文掌握iOS应用性能优化:内存、启动、渲染全方位解析

iOS应用的性能优化是一个系统性工程,涉及内存管理、启动速度、页面渲染等多个方面。本文将从这些维度出发,分享一系列实用的优化策略,帮助开发者打造更流畅、更高效的iOS应用。

一、内存管理与缓存策略

内存使用场景

  • 临时/局部:如二级页面的数据源缓存,使用完即释放。
  • 静态/全局:包括用static、const、extern声明的常量与对象(单例对象、全局数组)。

内存缓存策略

  • 常规缓存:NSDictionary、NSArray、NSSet、NSPointerArray / NSMapTable / NSHashTable(支持弱引用)。
  • 缓存+淘汰策略:LRU、LFU、NSCache(LFU 优先于 LRU)。

Xcode自带的内存检测工具

  • instruments-Allocations:查内存分配情况。
  • instruments-Leaks:动态内存分析。
  • Xcode-Product-Analyze:静态内存分析。

第三方开源的内存监控组件

  • Facebook的FBMemoryProfiler:分析内存使用和检测循环引用,仅检测OC。
  • 腾讯的OOMDetector:监控OOM、大内存分配、内存泄漏,支持C++对象和malloc内存块。

二、场景应用

1. 启动优化

iOS冷启动分为Pre-main与main两部分。相关资料推荐:抖音-iOS启动优化系列文章。

1.1 Pre-main

具体流程

  • Dyld:动态链接器,负责余下工作。
  • Load Dylibs:加载动态库(dylib与动态framework)。
  • Rebase:将镜像读入内存,修正指针,以Page为单位进行签名验证。
  • Bind:查询符号表,设置指向镜像外部的指针。
  • Objc:读取所有类与分类,检查selector的唯一性。
  • initalizers:运行程序的初始化函数。

优化策略

  • 合并自定义动态库,删除冗余的dylib。
  • 将动态库转为静态库。
  • 使用二进制重排。
  • 减少ObjC类、方法数量。
  • 减少+load方法调用。
  • 减少C的constructor函数、C++静态对象。
1.2 Main

具体流程:略

优化策略

  • SDK注册使用异步并发加载。
  • 避免过多串行接口操作。
  • 避免启动后出现过多耗性能操作。

2. 页面渲染优化

2.1 原生页面-渲染原理

View的渲染

View的展示由Layer实现,View主要处理Touch响应链相关的事件。当View/Layer的frame与图层结构发生改变,系统会在mainRunLoop的回调中遍历所有待处理View/Layer,实现UI的刷新。

底层渲染流程大致如下:

  1. 初始化用于绘制的上下文EAGLContext;
  2. 创建帧缓冲区和渲染缓冲区;
  3. 添加附件;
  4. 切换到帧缓冲区进行绘制;
  5. 切换到屏幕缓冲区读取信息;
  6. 绘制到屏幕上。

所有Bitmap最终都要由内存提交到显存,绑定为GPU Texture。

GPU的离屏渲染

  • 当前屏幕渲染:在当前屏幕缓冲区进行。
  • 离屏渲染:在额外开辟的缓冲区进行。

离屏渲染主要开销包括创建新的缓冲区、屏幕缓冲区到离屏缓冲区的来回切换。iOS中主要是由于Layer的某些属性设置导致的离屏渲染,如mask、opaque、shadow、rasterize、cornerRadius。

2.2 原生页面-复杂布局

原生页面的复杂布局常见场景:

  • 微博、朋友圈等样式多样化的列表页。
  • 股票K线图等图形绘制页面。

低复用列表优化

  • 避免离屏渲染:注意mask、opaque、shadow、rasterize、cornerRadius属性。
  • 减少视图过度绘制:Cell中的非交互图层使用Layer代替;减少图层嵌套。
  • 数据加载:使用懒加载/预加载;使用异步线程实现数据的获取/加工。
  • 图片预解码:把图片解码从主线程切换到子线程。
- (void)drawImage:(UIImage *)image {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CGImageRef imageRef = image.CGImage;
        size_t width = image.size.width;
        size_t height = image.size.height;
        size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
        size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
        CGColorSpaceRef space = CGImageGetColorSpace(imageRef);
        uint32_t bitmapInfo = CGImageGetBitmapInfo(imageRef);
        CGContextRef contextRef = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, space, bitmapInfo);
        CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
        CGImageRef tImageRef = CGBitmapContextCreateImage(contextRef);
        CGContextRelease(contextRef);
        dispatch_async(dispatch_get_main_queue(), ^{
            self.layer.contents = (__bridge id)tImageRef;
            CGImageRelease(tImageRef);
        });
    });
}

频繁画布重绘优化

  • 统一事件源触发:使用定时器与Touch事件源的统一入口。
  • 减少局部刷新:以整体数据源的变化为刷新频次。
  • 减少图层嵌套:使用效率更优的CGGraphis-API。
  • 使用异步绘制:在子线程绘制复杂的图层元素。
- (void)drawsAsynchronously:(void(^)(CGContextRef context))drawsBlock {
    CGSize size = self.bounds.size;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIGraphicsBeginImageContext(size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        drawsBlock(context);
        UIImage *tImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            self.layer.contents = (__bridge id)tImage.CGImage;
        });
    });
}
2.3 原生页面-动画效果

UI动画往往对性能的开销比较大,iOS项目中最常见的动画包括帧动画与核心动画。

  • UIImageView animations适用于帧数较少的场景。
  • Gif的播放对CPU与内存的开销较大,可以使用FLAnimatedImage/YYImage(本地)、SDWebImage(网络)。
  • Lottie是一个基于移动端和web端的跨平台动画框架,可以将设计好的动画导出成JSON格式,并在移动端和Web端实现动画的渲染。

动画的冲突也会出现明显的卡顿现象,可以通过异步调用的方式来规避。

核心动画包含基础动画、关键帧动画、组合动画、过度动画,可以直接调用系统提供的API实现。

2.4 Web页面优化

白屏时间长

  • 资源本地化:通过加载H5本地资源包或cdn资源拦截+本地映射。
  • 骨架屏:页面加载网络数据时展示页面大致结构。

图片展示

  • 上传压缩:减少网路加载时耗。
  • 图片占位:防止页面跳动。

2.5 网络加速

图片加载支持WebP

WebP是一种同时提供了有损压缩与无损压缩的图片文件格式,派生自影像编码格式VP8,由Google开发。

具体实现流程:

  1. 服务端支持图片的webp加载;
  2. 通过Hook文件下载API,给图片url添加后缀‘.webp’;
  3. 加载webp资源文件;
  4. SDWebImage自带webp解码器;
  5. webp解码成jpg/png,图片展示。

HttpDNS解析

HttpDNS解析是使用HTTP协议进行域名解析,代替现有基于UDP的DNS协议,能够避免Local DNS造成的域名劫持问题和调度不精准问题。

具体实现流程:

  1. 通过NSURLProtocol对请求进行重定向;
  2. 获取域名解析后的IP信息。
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号