《高性能 iOS 应用开发》知识点整理

1、性能指标

  • 内存
  • 电量消耗
  • 初始化时间
  • 执行速度
  • 响应速度
  • 本地存储
  • 互操作性(应用之间的互操作性、数据共享)
  • 网络环境
  • 带宽
  • 数据刷新
  • 多用户支持
  • 单点登录
  • 安全
  • 崩溃(Flurry 可用于收集崩溃信息)

2、日志三方库:CocoaLumberjack

3、栈与堆

应用中新创建的每个线程都有专用的栈空间,该空间由保留的内存和初始提交的内存组成。栈可以在线程存在期间自由使用。线程的最大栈空间很小,这就决定了以下的限制:

  • 可被递归调用的最大方法数:每个方法都有其自己的栈帧,并会消耗整体的栈空间。
  • 一个方法最多可以使用的变量个数:所有的变量都会载入放大的栈帧中,并消耗一定的栈空间。
  • 视图层级中可以嵌套的最大视图深度:渲染复合视图将在整个视图层级树中递归地调用 layoutSubViews 和 drawRect 方法,如果层级过深,可能会导致栈溢出。

每个进程的所有线程共享一个堆。一个应用可以使用的堆大小通常远远小于设备的 RAM 值,应用并不能控制分配给它的堆,只有操作系统可以管理堆。
使用 NSString、载入图片、创建或者使用 JSON/XML 数据、使用视图等都会消耗大量的堆内存。
通过类创建的对象相关的所有数据都存放在堆中。
当对象被创建并被赋值时,数据可能会从栈复制到堆。

4、autoreleasepool 使用场景:

  • 当有一个创建了很多临时对象的循环时
  • 当创建一个线程时

5、避免循环引用的规则

  • 对象不应该持有它的父对象,应该使用 weak 引用指向它的父对象。
  • 连接对象不应该持有它们的目标对象,目标对象的角色是持有者。连接对象包括:
    • 使用委托的对象;
    • 包含目标和 action 的对象;
    • 观察者模式中的被观察的对象。
  • 使用专用的销毁方法中断循环引用。

6、 NSTimer 和 NSThread

使用 NSTimer 和 NSThread 时,总是应该通过间接的层实现明确的销毁过程。这个间接层应该使用弱引用,从而保证所拥有的的对象能够在停止只用后执行销毁动作。

7、在Xcode 中设置严格的选择器匹配:

Target —> Build Settings —> Strict Selector Martching —> YES

8、依赖注入

依赖注入的两种方案:Typhoon 、Objection

9、网络:

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

10、GCD 提供的功能列表:

  • 任务或分发队列,允许主线程中的执行、并行执行和串行执行;
  • 分发组,实现对一组任务执行情况的跟踪,而与这些任务所基于的队列无关;
  • 信号量;
  • 屏障,允许在并行分发队列中创建同步的点;
  • 分发对象和管理源,实现更为底层的管理和监控;
  • 异步 I/O ,使用文件描述符或管道。

11、线程消耗

每个线程大约消耗 1KB 的内核内存空间,这块内存用于存储与线程相关的数据结构和属性,属于联动内存,无法分页。
主线程的栈空间大小为 1M,并且无法修改,其他二级线程默认分配 512KB 栈空间,但是并不会一次性创建完整大小,而是随着使用逐渐增长。栈空间的最小值是 16 KB ,而且其数值必须是 4 KB 的倍数。
线程创建的时间消耗为 4~5 毫秒,启动线程的时间消耗为 29 毫秒,主要是上下文切换带来的时间开销。
GCD最大线程个数为 64 个。

12、读写锁

将读写锁应用于并发读写:并行访问只读操作,写操作需要互斥访问。
1.创建一个并行队列;
2.在这个队列上使用 dispatch_sync 执行所有的读操作;
3.在同一个队列上使用 dispatch_barrier_sync 执行所有的写操作。

13、使用生成器实现不可变实体

对于数据实体的初始化,一般有两种方案:使用自定义初始化器;使用生成器模式。
自定义初始化器需要众多的参数,和很长的方法名,并且会带来向下兼容的问题,加入新的属性将导致调用初始化器的代码不能使用。因此建议使用生成器模式,核心代码如下:

+ (instancetype)userWithBlock:(void (^)(UserBulider *))block {
    UserBulider *builder=[[UserBulider alloc]init];
    block(builder);
    return [builder build];
}

14、单例/工厂

在实体层或者服务层使用单例并不是明智的选择,使用可配置的工厂要优于使用单例,工厂可以创建可销毁的单例。

15、响应式编程库

ReactiveCocoa ,可以实现一个观察变化的系统,这个系统有着低耦合、高伸缩性、自包含且适用于通用目的的特点,更重要的是它为链接(RACSignal)提供了 Promise,使得我们能够编写出更易于理解和维护的代码。
另外 PromiseKit 是支持使用 Promise 的另外一个库,它甚至做得更好,因为他可以帮助避免代码向右偏移,同时他也提供了优雅的错误处理能力,强烈建议深入研究 PromiseKit。

16、宏定义

Target —> Build Settings —> Preprocessor Macros 可以创建宏定义,如: DEBUG、RELEASE等。

17、静默通知和后台拉取

有时候,实际性能不如感知性能重要。为了使用户对应用有良好的的感知,聪明的做法是使用静默通知和后台拉取对应用进行热启动,这样可以为下次使用提前做好准备。

18、创建视图控制器需要遵循的一些基本的最佳实践:

  • 保持视图控制器轻量;
  • 不要在视图控制器中编写动画逻辑;
  • 使用数据源和委托协议,将代码按照数据检索、数据更新和其他的业务逻辑进行分离,视图控制器只能用来选择正确的视图,并将它们连接到供应源;
  • 视图控制器响应来自视图的事件,然后将它们连接到数据接收器;
  • 视图控制器响应来自操作系统的 UI 相关事件;
  • 不要编写自定义的 init 代码;
  • 创建一个实现了公共设置的基类视图控制器,其他视图控制器从这里继承就好;
  • 在各个视图控制器之间,使用 category 创建可以复用的代码。

19、在快速滚动视图时使用界面外壳,核心代码如下:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGPoint velocity = [self.tableView.panGestureRecognizer velocityInView:self.view];
    self.velocity=velocity;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (fabs(self.velocity.y)>2000) {
        //返回界面外壳
    }else{
        //返回真正的单元格cell
    }
}

20、drawInRect

从性能角度来看,在某些时候,直接绘图(drawInRect)提供的性能比复合视图提供的要好一个数量级,例如微博的页面。

21、Charles

使用 Charles 可以发送自定义响应,这是一个很有用的功能,可以在不打扰服务器的前提下测试各种可能的情况。要测试性能就发送大量的数据,要测试稳定性,就发送大量的数据以及无效的输入。

22、UIDocumentInteractionController

作为发布者,通过 UIDocumentInteractionController 类可以允许设备上的其他应用打开该应用的文档;
作为消费者想要打开其他应用的文档,需要两个步骤:
注册应用支持的文件类型(在Info.plist 文件中配置,包括名称、类型、图标和属性);
处理文档内容,通过在 AppDelegate 中实现代理方法 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options。

23、UIActivityViewController

使用 UIActivityViewController 可以比 UIDocumentInteractionController 更容易和灵活的完成应用间的分享和其他各种操作,对于不能使用三方分享工具的可以采取这个方案。

24、IDFV/IDFA

iOS 有两个选项可以用于识别设备:供应商标识符(IDFV)和广告商标识符(IDFA)。

25、OCMock

OCMock 是一个很好的 mocking 框架,支持创建 mock 和 spy 对象。

26、视图调试器

Xcode 提供的视图调试器(Debug 区域工具栏 —>Debug View Hierachy)在调试时主线程会暂停,使用 PonyDebugger 可以解决这个暂停的问题。

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

推荐阅读更多精彩内容