iOS架构设计和性能优化-以简单明了为直截

架构总结

Apple 版本 MVC架构总结

View的复用性 可重用性 不需要知道Model是什么

model发生了改变 View照样使用

8E8310AF539853EA232F817235C19ABE.png
优点 缺点
View Model可以重复利用 可以独立使用 Controller的代码过于臃肿

MVC变种_MVC瘦身

View中拥有一个模型

一个View绑定一个Model

-(void)setModel:(Model *)model
{
   _model = model;
}

View内部子控件 屏蔽掉 View更具封装性 缺点是依赖性 依赖Model

E083D8377343709083E7C2FB79BE7299.png
优点 缺点
对Controller瘦身 将View内部的细节屏蔽 外界不知道View内部的实现 View依赖于Model

MVP

Model-View-Presenter

FDD9F39E4FEC045ED1BE8D3D832321C0.png
优点 缺点
进一步Controller瘦身 P里代码容易臃肿 每个P对应一个MVC

MVVM

Model-View-ViewModel

C75EA54A2B145F08F659FDB0F5158AFA.png

与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) 等到离屏渲染结束以后 将离屏缓冲区的渲染结果显示到屏幕上 又需要将上下文环境从离屏切换到当前屏幕

  • 那些操作会触发离屏渲染?
  1. 光栅化 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分析各个类占用大小

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341