《高性能iOS应用开发》核心优化

一、移动应用的性能

性能指标

  • 内存

运行所需最小 RAM,内存平均值和峰值,内存泄漏

  • 电量消耗

  • 初始化时间

惰性初始化所需的对象

  • 执行速度

并行处理技术或将复杂任务分发到服务器

  • 响应速度

  • 本地存储

  • 互操作性

  • 网络环境

    • 高带宽稳定环境
    • 低带宽稳定环境
    • 高带宽不稳定环境
    • 低带宽不稳定环境
    • 无网络
  • 带宽

  • 数据刷新

  • 多用户支持

  • 单点登录

如果用户登录了一个应用,则只需要点击一次,就可以登录到其他应用

  • 安全

  • 崩溃

应用性能分析

  • 采样
  • 埋点
  • 日志(debug、verbose、info、warning、rrror)

二、内存管理

iOS 的虚拟内存模型不包含交换内存,意味着磁盘不能被用来分页内存。iOS 应用的内存消耗分为两部分:栈大小和堆大小。

每个进程的所有线程共享同一个堆,只有操作系统能管理堆。

循环引用常见场景

  • 委托

在委托中建立对操作的强引用,在操作中建立对委托的弱引用。

  • 线程与计时器

NSTimer 创建后会持有创建者,同时被 runtime 持有,可以调用 NSTimer 的 invalidate 方法解除资源和持有。

推荐方案:将持有关系分散到多个类中,任务类执行具体动作,所有者类调用任务。从而通过间接层实现明确的销毁过程。间接层使用弱引用持有所有者类,以保证所拥有的对象能够在停止使用后执行销毁动作。

  • 观察者

键值观察与通知中心均不会维持观察对象、被观察对象以及上下文的强引用,如有必要,需要自行维护。

返回错误

对象指针本身不会对对象的引用计数造成影响,对象指针与对象本身的内存管理修饰符应当一样。

{
    NSError *error = nil;
    
    @autoreleasepool {
        NSError * __weak errorWeak = error;
        [self performOperationWithError:&errorWeak];
    }
    
    NSLog(@"%@", error);
}

- (BOOL)performOperationWithError:(NSError *__weak*)error
{
    // 方法执行发生错误
    *error = [NSError errorWithDomain:@"MyAppDomain" code:123 userInfo:nil];
    return NO;
}

NSError** 作为参数时应该使用 __autoreleasing 修饰符。虽然赋值对象指针时,所有权修饰符必须一致,但是这里传参时编译器会进行优化

NSError *error = nil;
NSError __autoreleasing *tmp = error;
BOOL result = [obj performOperationWithError:&tmp];
error = tmp;

单例

单例使用场景

  • 队列操作(如日志或埋点)
  • 访问共享资源(如缓存)
  • 资源池(如线程池或连接池)

三、能耗

计算量的能耗取决于不同因素

  • 对数据的处理
  • 待处理的数据大小 —— 更大的显示器意味着更多的信息和数据需要处理
  • 处理数据的算法和数据结构
  • 执行更新的次数

最佳实践

  • 针对不同情况选择优化算法

小于 43 个实例则插入排序优于归并排序,多于 286 个则快速排序最优,优先使用双枢轴快排。

  • 尽量将数据处理放在服务器端
  • 优化静态编译处理(AOT)
  • 分析电量消耗

网络

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

推荐使用 Reachability Pod 检测网络状态。

使用基于队列的网络请求可以避免服务器遭到多个同时发起的请求轰炸。至少使用两个队列:一个用于通常不关键的大量图片下载,一个用于关键数据请求。

定位管理器

使用 GPS 坐标需要两点信息

  • 时间锁——每个 GPS 卫星每毫秒广播唯一一个 1023 位随机数,GPS 接收芯片必须正确与时间锁对齐
  • 频率锁——GPS 接收器计算由接收器与卫星的相对运动导致的多普勒偏移带来的信号误差

通常情况下锁定一颗卫星需要至少 30 秒,确定卫星越多,取得的坐标越精确。

只在必要时使用网络

每当应用建立网络连接时,硬件都会在连接完成后多维持几秒的活动时间,集中的网络通信都会消耗大量的电量。所以应该定期集中短暂地使用网络。

(可是埋点、网络监测模块应该怎么取舍呢?)

屏幕

  • 动画在应用进入后台后应该暂停
  • 视频播放时保持屏幕常亮

最佳实践

  • 最小化硬件使用,尽可能晚地与硬件打交道,一旦完成任务立即结束使用
  • 进行密集型任务前检查电池电量和充电状态
  • 电量低时提示用户是否确定要执行任务,并在用户同意后再执行
  • 提供设置选项,允许用户定义电量的阈值,以便在执行密集型操作前提示用户

四、并发编程

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

主线程的栈空间大小为 1M,并且无法修改,其他二级线程默认分配 512KB 栈空间,但是并不会一次性创建完整大小,而是随着使用逐渐增长。

线程创建的时间消耗为 4~5 毫秒,启动线程的时间消耗为 29 毫秒,主要是上下文切换带来的时间开销。

GCD最大线程个数为 64 个。

并行读取、互斥写入

  • 创建一个并行队列
  • 对于读操作使用 dispatch_sync
  • 对于写操作使用 dispatch_barrier_sync

dispatch_barrier_sync 可以在并行队列上创建一个同步点,dispatch_barrier_sync 中的代码块会在之前所有提交代码块执行结束后单独执行。

生成器模式

对于数据实体的初始化,一般有两种方案:

  • 使用自定义初始化器
  • 使用生成器模式

自定义初始化器需要众多的参数,和很长的方法名,并且会带来向下兼容的问题,加入新的属性将导致调用初始化器的代码不能使用。

因此建议使用生成器模式。

交叉引用

实体之间的交叉引用,尤其是不可变实体的交叉引用,会在初始化时陷入循环初始化陷阱,A 需要 B 来初始化,B 又需要 A 来初始化。此时可以利用冰棒不变性,用一个标志位和一个设置标志位的方法来进行一次性初始化操作,之后再对属性赋值时需要检测此属性来决定是否可以修改。

- (void)freeze
{
    self.frozen = YES;
}

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

推荐阅读更多精彩内容