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

2018-03-12  本文已影响47人  Yasic

一、移动应用的性能

性能指标

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

惰性初始化所需的对象

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

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

应用性能分析

二、内存管理

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 个则快速排序最优,优先使用双枢轴快排。

网络

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

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

定位管理器

使用 GPS 坐标需要两点信息

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

只在必要时使用网络

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

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

屏幕

最佳实践

四、并发编程

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

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

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

GCD最大线程个数为 64 个。

并行读取、互斥写入

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

生成器模式

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

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

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

交叉引用

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

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

- (void)setUserId:(NSString *)userId
{
    if (!self.frozen) {
        _userId = userId;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读