美文网首页
iOS架构设计和性能优化-以简单明了为直截

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

作者: LD_左岸 | 来源:发表于2019-03-07 15:40 被阅读0次

    架构总结

    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分析各个类占用大小

    相关文章

      网友评论

          本文标题:iOS架构设计和性能优化-以简单明了为直截

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