iOS架构设计和性能优化-以简单明了为直截
架构总结
Apple 版本 MVC架构总结
View的复用性 可重用性 不需要知道Model是什么
model发生了改变 View照样使用
8E8310AF539853EA232F817235C19ABE.png优点 | 缺点 |
---|---|
View Model可以重复利用 可以独立使用 | Controller的代码过于臃肿 |
MVC变种_MVC瘦身
View中拥有一个模型
一个View绑定一个Model
-(void)setModel:(Model *)model
{
_model = model;
}
E083D8377343709083E7C2FB79BE7299.pngView内部子控件 屏蔽掉 View更具封装性 缺点是依赖性 依赖Model
优点 | 缺点 |
---|---|
对Controller瘦身 将View内部的细节屏蔽 外界不知道View内部的实现 | View依赖于Model |
MVP
FDD9F39E4FEC045ED1BE8D3D832321C0.pngModel-View-Presenter
优点 | 缺点 |
---|---|
进一步Controller瘦身 | P里代码容易臃肿 每个P对应一个MVC |
MVVM
C75EA54A2B145F08F659FDB0F5158AFA.pngModel-View-ViewModel
与MVP相比 特色的是属性监听绑定 View监听ViewModel的数据改变
View拥有ViewModel ViewModel中拥有Model属性 View监听到ViewModel的改变实时刷新View
三层架构 四层架构
界面层 ——> 业务层(网络层加载数据) ——> 数据层
性能优化
CPU GPU
CPU 计算 文本的排版 图片的格式转换和解码 图像的绘制(Core Graphics)
GPU 渲染
纹理的渲染
帧缓存 —>视频控制器——>屏幕
卡顿优化
- 卡顿产生的原因
CPU GPU
丢帧 掉帧 卡顿 VSync来了 GPU没完事
- 卡顿解决的主要思路
尽可能减少CPU GPU资源消耗
按照60FPS的刷帧率 没隔16ms(1s = 1000 / 60)就会有一次VSync信号
- CPU优化
1.尽量使用轻量级的对象 比如用不到时间处理的地方 可以考虑使用CVLayer取代UIView
2.不要频繁调整UIView的相关属性 比如Frame Bounds Transform等属性 尽量减少不必要的修改
3.Autolayout会比直接设置frame消耗更多的CPU
4.图片的size最好刚好跟UIImageView的size保持一致
5.控制一下线程的最大并发数量
6.尽量把耗时的操作放到子线程 文本绘制计算 图片处理(解码 绘制)
UIImage imageName:@“”
显示之前去子线程解码 异步解码 主线程就少了解码的操作
- (void)image
{
UIImageView *imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(100, 100, 100, 56);
[self.view addSubview:imageView];
self.imageView = imageView;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 获取CGImage
CGImageRef cgImage = [UIImage imageNamed:@"timg"].CGImage;
// alphaInfo
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage) & kCGBitmapAlphaInfoMask;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaLast ||
alphaInfo == kCGImageAlphaFirst) {
hasAlpha = YES;
}
// bitmapInfo
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
// size
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
// context
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo);
// draw
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
// get CGImage
cgImage = CGBitmapContextCreateImage(context);
// into UIImage
UIImage *newImage = [UIImage imageWithCGImage:cgImage];
// release
CGContextRelease(context);
CGImageRelease(cgImage);
// back to the main thread
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = newImage;
});
});
}
- GPU优化
1.尽量减少视图数量和层次
2.尽量避免短时间内大量图片的显示 尽可能将多张图片合成一张显示
3.GPU能处理的最大纹理尺寸是4096 * 4096一旦超过这个尺寸 就会占用CPU资源进行处理 尽可能不要超过这个尺寸
4.减少透明的视图(alpha < 1)不透明的就设置opque = YES
5.尽量避免出现离屏渲染
离屏渲染
在OpenGL中 GPU有两种渲染方式
On-Screen Rendering : 当前屏幕渲染 在当前用于显示的屏幕缓冲区进行渲染操作
Off-Screen Rendering:离屏渲染 在当前屏幕的缓冲区以外新开辟一个缓冲区进行渲染操作 Apple的双缓冲 还不够用
- 离屏渲染消耗性能的原因
1.需要开辟新的缓冲区
2.离屏渲染的整个过程 需要多次切换上下文环境 先是从当前屏幕(On-Screen)切换到离屏(Off-Screen) 等到离屏渲染结束以后 将离屏缓冲区的渲染结果显示到屏幕上 又需要将上下文环境从离屏切换到当前屏幕
- 那些操作会触发离屏渲染?
- 光栅化 layer.shouldRasterize = YES
2.遮罩 layer.mask
3.圆角 同时设置 layer.masksToBounds = YES layer.cornerRadius 大于0
考虑通过CoreGraphics绘制裁减圆角 美工出图
4.阴影 layer.shadowXXX
设置layer.shadowPath就不会产生离屏渲染
- 卡顿检测
平时所说的卡顿 主要是因为在主线程执行了比较耗时的操作
可以添加Observer到主线程RunLoop中 通过监听RunLoop状态切换的耗时 以达到监控卡顿的目的
电量优化
- 耗电的主要来源
CPU处理 Processing
网络 Networking
定位 Location
图像 Graphice - 耗电优化
1.尽可能降低CPU GPU功耗
2.少用定时器
3.优化I/O操作 文件操作 读写
4.尽量不要频繁写入小数据 最好一次性写入
读写大量主要的数据时 考虑用dispatch_io 其提供了基于GCD的异步文件I/O的API 用dispatch_io系统会优化磁盘访问
数据量比较大的 建议用数据库(比如SQLite CoreDate)
5.网络优化 减少压缩网络数据 XML体积大 JSON体积小
protobuf protocol buffer
如果多次请求的结果是相同的 尽量使用缓存
使用断点续传 否则网络不稳定时可能多次传输相同的内容
网络不可用的时候 不要尝试执行网络请求
6.让用户可以取消长时间运行或者速度很慢的网络操作 设置合适的超时时间
网络遮罩 左上角返回按钮
7.批量传输 比如下载视频流时 不要传输很小的数据包 直接下载整个文件或者一大块一大块的下载 如果下载广告 一次性多下载一些 然后在慢慢展示 如果下载电子邮件 一次下载多封 不要一封一封的下载
8.定位优化
如果只是需要快速确定用户的具体位置 最好用CLLocationManager 的requestLocation方法 定位完成后 会自动让定位硬件断电
CoreLocation
CLLocationManger * mgr
如果不是导航类应用 尽量不要实时更新位置 定位完关闭定位服务
尽量不要使用CLLocationAccuracy精准度最高的KCLLocationAccuracyBest
需要后台定位时 尽量设置pausesLocationUpdatesAutomatically为YES 如果用户不太可能移动的时候系统会自动暂停位置更新
mgr.pausesLocationUpdatesAutomatically = YES;
APP的启动优化
App的启动可以分为两种
冷启动 (Cold Launch) 从零开始启动App
热启动(Warm Launch)App已经存在内存中 在后台活着 再次点击图标启动App App的启动时间的优化 主要是真多冷启动进行优化
通过添加环境变量可以打印出App的启动时间分析(Edit scheme ->Run ->Arguments)
DYLD_PRINT_STATISTICS 设置为1
DYLD_PRINT_STATISTICS_DETAILS
400ms以内正常
App的冷启动主要概括为3大阶段
dyld (dynamic link editor) Apple的动态链接器 可以用来装载Mach-O文件(可执行文件 动态库等)
可执行文件不包含动态库 只是 包含的依赖关系 含有那些依赖
启动APP时 dyld所做的事情有
装载APP的可执行文件 同时会递归加载所有依赖的动态库
当dyld把可执行文件 动态库加载完毕后 会通知Runtime进行下一步的处理
App的启动 -runtime
启动App时 runtime所做的事情有
调用map_images 进行可执行文件内容的解析和处理
在load_images中调用call_load_methods 调用所有的Class和Category的+load方法
进行各种objc结构的初始化(注册Objc类 初始化类对象等等)
调用C++静态初始化器和__attribute((constructor))修饰的函数
到此为止 可执行文件和动态库中所有的符号(Class Protocol Selector IMP…..)都已经按格式成功加载到内存中 被runtime所管理
总结
总结
App的启动由dyld主导 将可执行文件加载到内存 顺便加载所有依赖的动态库
并由runtime负责加载objc定义的结构
所有初始化工作结束后 dyld就会调用main函数
接下来就是UIApplicationMain函数 AppDelegate的application:didFinishLaunchingWithOptions:方法
********************按照不同的阶段*********************
-
dyld
减少动态库 合并一些动态库 (定期清理不必要的动态库)
减少Objc类 分类的数量 减少selector数量 (定期清理不必要的类 分类)
减少C++虚函数数量
Swift尽量使用Struct -
Runtime
用+initialize 方法 和 dispatch_once取代所有的_attribute((constructor)) C++静态构造器 Objc的+load -
Main
在不影响用户体验的前提下 尽可能将一些操作延迟 不要全部都放在finishLaunching方法中
按需要加载
****************************安装包瘦身*****************
用户下载时间 上传商店时间 都长 编译的效率
安装包组成
可执行文件
资源(图片 音频 视频)
采取无损压缩
去除没有用到的资源可执行文件瘦身
编译器优化
Strip Linked Product
Make Strings Read-Only
Symbols Hidden by Default
设置为YES
利用AppCode 检测未使用的代码LinkMap分析各个类占用大小