《高性能iOS应用开发》---笔记

一般,评判一个app的性能好与坏,有以下几个方面:
内存、电量消耗、初始化时间、执行速度、响应速度、本地存储、互操作性、网络环境、带宽、数据刷新、多用户支持、单点登录、安全、崩溃

核心优化

内存管理

内存消耗,指的是应用消耗的RAM
RAM的使用者不仅包括在前台运行的应用,还包括操作系统服务,甚至还包括其他应用所执行的后台任务。

应用中的内存消耗分为两部分:栈大小堆大小

栈大小

应用中,新创建的每个线程都有专用的栈空间
栈决定了:

  • 可被递归调用的最大方法数

方法调用,使用到了栈

  • 视图层级中可以嵌入的最大视图深度
    视图层级树中递归调用layputSubViews和drawRect方法。其实,还是递归
堆大小

每个进程的所有线程共享一个堆
使用NSString、载入图片、创建或者使用JSON/XML数据、使用视图等都会消耗大量的堆内存。

在这里插入图片描述
5. i 的值在栈上。但赋值给属性时,它必须被复制到堆中,因为那是存储 result 的地方。

result对象是通过new创建的,因此,result对象存储在堆上
result存储在堆上,result上面的属性、变量也存储在堆上

8.使用 anInteger 时,它的值必须先复制到栈然后才能进行进一步的处理。

items是传过来的值,在栈上
但items指向的对象,即items对象是在堆上
因此,objc在堆上
objc.anInteger在堆上
但是total是局部变量,因此objc.anInteger需要从堆复制到栈上面。

内存管理模型

内存管理模型基于持有关系的概念。如果一个对象正处于被持有状态,那它占用的内存就不能被回收。

变量限定符

ARC为变量提供了四种生命周期限定符

  • __strong
    这是默认的限定符,无需显示引入
  • __weak
    这表明引用不会保持被引用对象的存活。当没有强引用指向对象时,弱引用会被置为nil。
  • __unsafe_unretained
    与__weak类似,只是当没有强引用指向的对象时,__unsafe_unretained不会置为nil
  • __autoreleasing
    __autoreleasing用于由引用使用id * 传递的消息参数。

通过以下几点,可以很大程度上避免许多麻烦,如内存泄漏、循环引用和较大内存消耗。

  • 避免大量的单例。
  • 对子对象使用__strong
  • 对父对象使用__weak
  • 使引用图闭合的对象(如委托)使用__weak
  • 对数值属性(NSInteger、SEL、CGFloat等)而言,使用assign限定
  • 对于块属性,使用copy限定符
  • 进行必要清理时遵循以下准则:销毁计时器、移除观察者、解除回调

能耗

设备中的每个硬件模块都会消耗电量。电量的最大消费者是CPU,除了COU外,还需要关注的硬件模块包括:网络硬件、蓝牙、GPS、麦克风、加速计、摄像头、扬声器和屏幕

应用计算得越多,消耗的电量就越多。(在完成相同的基本操作时,老一代的设备会消耗更多的电量)。计算量的消耗取决于不同的因素。

  • 对数据的处理
  • 待处理的数据大小
  • 处理数据的算法和数据结构
  • 执行更新的次数,尤其是在数据更新后,触发应用的状态或UI进行更新。

没有单一规则可以减少设备中的执行次数。以下可以尝试的有:

  • 针对不同的情况,选择优化的算法
  • 如果应用从服务器接收数据,尽量减少需要在客户端进行的处理,
  • 优化静态编译处理

蜂窝无线系统对电量的消耗远大于WIFI信号。因此,避免在没有连接WIFI的情况下进行高宽带消耗的操作。
需要程序员做的有:

  • 在进行网络操作之前,检查合适的网络连接是否可用。
  • 持续监听网络的可用性,并在连接状态发生变化时给予适当的反馈。

并发编程

线程

线程 是运行时执行的一组指令序列。
每个进程至少应包含一个线程。

虽然应用有多个线程看起来非常不错,但每个线程都有一定的开销,从而影响到应用的性能。线程不仅仅有创建时的时间开销,还会消耗内核的内存,即应用的内存空间。

每个线程大约消耗1KB的内核内存空间。这块内存用于存储于线程有关的数据结构和属性。这块内存是联动内存,无法被分页。

主线程的栈空间大小为1M,而且无法修改。所有的耳机线程默认分配512KB的栈空间。
完整的栈并不会立即被创建出来。实际的栈空间大小会随着使用而增长。因此,即使主线程有1MB的栈空间,某个时间点的实际栈空间很可能要小很多。

在线程启动前,栈空间的大小可以被改变。栈空间的最小值是16KB,而且其数值必须是4KB的倍数。

GCD

GCD提供的功能列表:
  • 任务或分发队列,允许主线程中的执行、并行执行和串行执行。
  • 分发组,
  • 信号量;
  • 分发对象和管理源,实现更为底层的管理和监控;
  • 异步I/O(读写);

GCD的线程池上限为64个

应用的生命周期

应用有四种启动类型:首次启动、冷启动、热启动、升级后的启动

用户界面

在这里插入图片描述

视图控制器

一些建议:

  • 保持视图控制器的轻量。在MVC中,控制器只是纽带,而不是存放所有业务逻辑的地方。它甚至不属于模型。
  • 不在在视图控制器中编写动画逻辑。动画可以在独立的动画类中实现,该类接受视图作为参数传入。
  • 使用数据源和委托协议,将代码按照数据检索、数据更新和其他业务逻辑进行分离。视图控制器只能用来选择正确的视图,并将它们连接到供应源。
  • 视图控制器相应来自视图的事件,如按钮点击事件或者列表单元格的选择事件,然后将它们连接至数据接收器。

之前一直在建立基于Controller的控制器,基于UIView的视图,基于NSObject的数据。现在觉得,要多用基于NSObject的各种事件、逻辑、业务。和基于委托的各种协议,将Controller释放出来。

  • 视图控制器响应来自操作系统的UI相关事件,如方向变化或低内存警告⚠️。这可能会触发视图的重新布局。
  • 不要在视图控制器中布局UI

对苹果的MVC模型,以前理解的是:
控制器里面放一切逻辑相关的东西。即使把某些东西抽离出来,代码量又不会变小,系统编译速度不还是一样吗?不会影响程序运行速度。

现在的理解:
虽然代码量没有因为抽离、封装而变得小,但是,
抽离会使得代码的复用变得可能
并且,可以很好的去扩展,不至于业务逻辑变化,需要把整个控制器推翻,更易于维护,这岂不是解决了公司的成本,也节省了程序员自己的工作量。
代码更有条理性,也更方便阅读,不至于看到一个控制器5000+行而去害怕。

  • 避免在视图层次结构中多层嵌套。尽量保持扁平化。
    在层次结构的任何位置添加视图时,它的祖先树节点会执行值为YES的setNeedsLayout:方法,当事件队列正在执行时,该设置会触发layoutSubviews:。这个调用代价较大,因为视图必须根据约束重新计算子视图的位置,而且在祖先树的每一层都会发生这种情况。

  • 尽可能延迟加载视图并进行重用。更多的视图不仅会导致加载时间变长,还会使渲染时间变长,这些会影响内存和CPU的使用。

也就是视图尽量使用懒加载,用到时再加载到内存,而不是一股脑全部加载到内存。以防发生卡顿

  • 对于已知的图像,使用imageNamed:方法加载图像。它可以确保内容只被加载至内存一次(然后存在内存中)

  • 如果加载一个大图像,考虑使用:imageWithContentsOfFile:,代替资源目录和imageNamed:方法,因为资源目录缓存了这些图片

  • UITableViewDataSsource,将dataSource属性设置到数据源上

  • UITableViewDelegate,当用户与列表或单元格交互时

  • 在移动应用的上下文中,深层连接包括使用统一的资源标识符(uniform resource identifier,URI),其连接到移动应用内的特定位置,而不是简单地启动应用。

  • 深层连接为应用之间的共享数据提供了解耦的方案。与访问网站时的HTTP网址类似,iOS中的深层连接通过所谓的自定义URL scheme来提供。你可以配置自己的应用,让它响应唯一的scheme


测试

测试类型

测试类型分为:

  • 单元测试
    在模拟环境中,测试一个独立方法来保证其有效性
  • 功能测试
    在真是环境中测试一个方法,来确保准确性
  • 性能测试
    测试一个方法、模块或者完整应用的性能

其他定义:

  • 测试用例:需要进行测试的一个场景。
  • 测试报告:测试成功或者失败的内容摘要
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值