iOS包罗万象之底层
目录
1、连接器
2、极速编译调试
3、静态分析 工具。
4、如何利用 Clang 为 App 提质?
5、无侵入的埋点方案如何实现?
6、多线程的那些坑
7、耗电优化
8、RunLoop精讲
9、PromiseKit 异步事件链编程 PromiseKit
10、simdjson高性能JSON解析框架 https://time.geekbang.org/column/article/93819 (simdjson 是一款他们研究了很久的快速 JSON 解析器, 号称每秒可解析千兆字节 JSON 文件。)
11、富文本渲染:(WebView 、 YYText)网页预加载框架 STMURLCache
12、测试框架:BDD 的 Objective-C 框架有 kiwi、Specta、Expecta等,Swift 框架有 Quick。
13、开发代码规范
14、iOS学习资料(高级)
15、iOS内存管理 (第三方内存检测工具有 MLeaksFinder、FBRetainCycleDetector、OOMDetector OOMDetector。)
0、进阶开发
1、iOS开发提高(唐巧)https://blog.devtang.com/2014/07/27/ios-levelup-tips/
2、苹果官方教程:https://developer.apple.com/tutorials/SwiftUI
3、APPdemo:https://www.appcoda.com
4、底层知识是最值得深挖的,不管哪个领域,殊途同归,底层都是需要持续学习的。这里我推荐 Michael Ash 的:https://www.mikeash.com/book.html
1、链接器:符号是怎么绑定到地址上的?
1>iOS 编写的代码是先使用编译器把代码编译成机器码,然后直接在 CPU 上执行机器码的。之所以不使用解释器来运行代码,是因为苹果公司希望 iPhone 的执行效率更高、运行速度能达到最快。文件越多,链接器链接 Mach-O 文件所需绑定的遍历操作就会越多,编译速度也会越慢。
2>解释器可以在运行时去执行代码,说明它具有动态性,程序运行后能够随时通过增加和更新代码来改变程序的逻辑
2、App 如何通过注入动态库的方式实现 极速编译调试?动态库链接器的实际应用,也就是编译调试的提速问题。
1>iOS 原生代码的 编译调试,都是通过一遍又一遍地编译重启 App 来进行的。所以,项目代码量越大,编译时间就越长。虽然我们可以通过 「将部分代码先编译成二进制」集成到工程里,来避免每次都全量编译来加快编译速度
2> 原生代码怎样才能够实现动态极速调试
1》Swift Playground
2》Flutter Hot Reload
3》Injection for Xcode :John Holdsworth 开发了一个叫作 Injection 的工具可以动态地将 Swift 或 Objective-C 的代码在已运行的程序中执行,以加快调试速度,同时保证程序不用重启。作者已经开源了这个工具,地址是GitHub 。使用方式就是 clone 下代码,构建 InjectionPluginLite/InjectionPlugin.xcodeproj ;删除方式是,在终端里运行下面这行代码:rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin
Injection 会监听源代码文件的变化,如果文件被改动了,Injection Server 就会执行 rebuildClass 重新进行编译、打包成动态库,也就是 .dylib 文件。编译、打包成动态库后使用 writeSting 方法通过 Socket 通知运行的 App。
Server 会在后台发送和监听 Socket 消息,实现逻辑在 InjectionServer.mm 的 runInBackground 方法里。Client 也会开启一个后台去发送和监听 Socket 消息,实现逻辑在 InjectionClient.mm里的 runInBackground 方法里。Client 接收到消息后会调用 inject(tmpfile: String) 方法,运行时进行类的动态替换。inject(tmpfile: String) 方法的具体实现代码,你可以点击这个链接查看GitHub。
3、其他方法:可做参考 其他提高编译速度方案
3、Clang、Infer 和 OCLint ,我们应该使用谁来做 静态分析?随着业务开发迭代速度越来越快,完全依赖人工保证工程质量也变得越来越不牢靠。所以iOS 开发者需要一种代码调试技术。
Clang1>OCLint:(你可以在官方规则索引中,查看完整的规则说明。)OCLint 是基于 Clang Tooling 开发的静态分析工具,主要用来发现编译器检查不到的那些潜在的关键技术问题。2017 年 9 月份新发布的 OCLint 0.13 版本中,包含了 71 条规则。这些规则已经基本覆盖了具有通用性的规则,主要包括语法上的基础规则、Cocoa 库相关规则、一些约定俗成的规则、各种空语句检查、是否按新语法改写的检查、命名上长变量名短变量名检查、无用的语句变量和参数的检查。除此之外,还包括了和代码量大小是否合理相关的一些规则,比如过大的类、类里方法是否太多、参数是否过多、Block 嵌套是否太深、方法里代码是否过多、圈复杂度的检查等。
OCLint 的安装方式,我建议你使用 Homebrew 的方式。Homebrew 是 macOS 下专门用来进行软件包管理的一个工具,使用起来很方便,让你无需关心一些依赖和路径配置。使用 Homebrew 的方式安装时,我们需要首先设置 brew 的第三方仓库,然后安装 OCLint。安装方法是在终端输入:
1 brew tap oclint/formulae 2 brew install oclint
安装完成,先编写一个 Hello world 代码来测试下,创建一个 Hello.m 文件来编写代码,使用 OCLint 来检查下前面编写的 Hello.m ,在终端输入如下命令:oclint Hello.m
然后,我们可以使用下面的命令,将检查结果生成为一个 HTML 格式的报告:oclint -report-type html -o report.html Hello.m
2>Clang 静态分析器:Clang 静态分析器(Clang Static Analyzer)是一个用 C++ 开发的,用来分析 C、C++ 和 Objective-C 的开源工具,是 Clang 项目的一部分,构建在 Clang 和 LLVM 之上。Clang 静态分析器的分析引擎用的就是 Clang 的库。
你可以点击这里这里下载 Clang 静态分析器,然后 解压就可以了,不需要放到特定目录下。而卸载它的话,删除这个解压后的目录即可。
infer安装scan-build 的原理是,将编译器构建改成另一个“假的”编译器来构建,这个“假的”编译器会执行 Clang 来编译,然后执行静态分析器分析你的代码。scan-build 的使用方法,也很简单,你只需要到项目目录下,使用如下命令即可:
\yourpath\scan-build -k -V make
关于 scan-build 的更多参数和使用说明,你可以点击这个链接查看。
3> Infer:Infer 是 Facebook 开源的、使用 OCaml 语言编写的静态分析工具,可以对 C、Java 和 Objective-C 代码进行静态分析,可以检查出空指针访问、资源泄露以及内存泄露。
Infer 的安装,有从源码安装和直接安装 binary releases 两种方式。
infer使用使用源码安装所需的时间会比较长,因为会编译一个特定的 Clang 版本,而 Clang 是个庞大的工程,特别是第一次编译的耗时会比较长。我在第一次编译时,就大概花了一个多小时。所以,直接安装 binary releases 会更快些,在终端输入:
brew install infer
如何使用 Infer。我们可以先写一段 Objective-C 代码:
Infer 的工作原理:
第一个阶段是转化阶段,将源代码转成 Infer 内部的中间语言。类 C 语言使用 Clang 进行编译,Java 语言使用 javac 进行编译,编译的同时转成中间语言,输出到 infer-out 目录。
第二个阶段是分析阶段,分析 infer-out 目录下的文件。分析每个方法,如果出现错误的话会继续分析下一个方法,不会被中断,但是会记录下出错的位置,最后将所有出错的地方进行汇总输出。
默认情况下,每次运行 infer 命令都会删除之前的 infer-out 文件夹。你可以通过 --incremental 参数使用增量模式。增量模式下,运行 infer 命令不会删除 infer-out 文件夹,但是会利用这个文件夹进行 diff,减少分析量。一般进行全新一轮分析时直接使用默认的非增量模式,而对于只想分析修改部分情况时,就使用增量模式。
Infer 检查的结果,在 infer-out 目录下,是 JSON 格式的,名字叫做 report.json 。生成 JSON 格式的结果,通用性会更强,集成到其他系统时会更方便。
4> 总结:其中 Clang 静态分析器和 Xcode 的集成度高,也支持命令行。不过,它们检查的规则少,基本都是只能检查出较大的问题,比如类型转换问题,而对内存泄露问题检查的侧重点则在于可用性。
OCLint 检查规则多、定制性强,能够发现很多潜在问题。但缺点也是检查规则太多,反而容易找不到重点;可定制度过高,导致易用性变差。
Infer 的效率高,支持增量分析,可小范围分析。可定制性不算最强,属于中等。综合来看,Infer 在准确性、性能效率、规则、扩展性、易用性整体度上的把握是做得最好的,我认为这些是决定静态分析器好不好最重要的几点。所以,我比较推荐的是使用 Infer 来进行代码静态分析。
4、如何利用 Clang 为 App 提质?
1> 基于 Clang 还可以开发出用于代码增量分析、代码可视化、代码质量报告来保障 App 质量的系统平台,比如 CodeChecker。
比如,当周末发现线上问题时,你会发现很多时候分析问题的人都不在电脑边,无法及时处理问题。这时,我们就需要一款在线网页代码导航工具,比如 Mozilla 开发的 DXR,方便在便携设备上去操作、分析问题,这样的工具都是基于 Clang 开发的。
2>Clang有点:第一,对于使用者来说,Clang 编译的速度非常快,对内存的使用率非常低,并且兼容 GCC ;第二,对于代码诊断来说, Clang 也非常强大,Xcode 也是用的 Clang;第三,Clang 对 typedef 的保留和展开也处理得非常好;第四,Fix-it 提示也是 Clang 提供的一种快捷修复源码问题的方式。在宏的处理上,很多宏都是深度嵌套的, Clang 会自动打印实例化信息和嵌套范围信息来帮助你进行宏的诊断和分析;第五,Clang 的架构是模块化的。除了代码静态分析外,利用其输出的接口还可以开发用于代码转义、代码生成、代码重构的工具,方便与 IDE 进行集成。
缺点:GCC 对于 Objective-C 的支持比较差,效率和性能都没有办法达到苹果公司的要求,而且它还难以推动 GCC 团队。
于是,苹果公司决定自己来掌握编译相关的工具链,将天才克里斯·拉特纳(Chris Lattner)招入麾下后开发了 LLVM 工具套件,将 GCC 全面替换成了 LLVM。这,也使得 Swift 这门集各种高级语言特性的语言,能够在非常高的起点上,出现在开发者面前。(苹果就是牛!)你可以点击这里的链接,在线查看 Clang 源码。
3>Clang提供的能力:Clang 为一些需要分析代码语法、语义信息的工具提供了基础设施。这些基础设施就是 LibClang、Clang Plugin 和 LibTooling。
5、无侵入的埋点方案如何实现?
封装hook1、解决问题:一是了解用户使用 App 的行为,二是降低分析线上问题的难度。目前,iOS 开发中常见的埋点方式,主要包括 「代码埋点」(手动写代码埋入)、「可视化埋点」(就是将埋点增加和修改的工作可视化了,提升了增加和维护埋点的体验)和 「无埋点」(运行时方法替换方式进行埋点)这三种。
2、最常见的三种埋点,就是对 「页面进入次数」、「页面停留时间」、「点击事件」的埋点。
3、这个方法利用运行时 method_exchangeImplementations 接口将方法的实现进行了交换,原方法调用时就会被 hook 住,从而去执行指定的方法。
VC页面统计埋点4、页面进入次数、页面停留时间都需要对 UIViewController 生命周期进行埋点。
区别不同的 UIViewController 呢?我一般采取的做法都是,使用 NSStringFromClass([self class]) 方法来取类名。这样,我就能够通过类名来区别不同的 UIViewController 了。
事件5、事件统计:对于点击事件来说,我们也可以通过运行时方法替换的方式进行无侵入埋点。这里最主要的工作是,找到这个点击事件的方法 sendAction:to:forEvent:,然后在 +load() 方法使用 SMHook 替换成为你定义的方法。
UIButton 在一个视图类中可能有多个不同的继承类,相同 UIButton 的子类在不同视图类的埋点也要区别开。所以,我们需要通过 “action 选择器名 NSStringFromSelector(action)” +“视图类名 NSStringFromClass([target class])”组合成一个唯一的标识,来进行埋点记录。
6、事件唯一标识:UITableViewCell 需要使用 indexPath,这个值里包含了 section 和 row 的值。所以,我们可以通过 indexPath 来确定每个 Cell 的唯一性。除了 UITableViewCell 这种情况之外, UIAlertController 也比较特殊。它的特殊性在于视图层级的不固定,因为它可能出现在任何页面中。但是,我们都知道它的功能区分往往通过弹窗内容来决定,所以可以通过内容来确定它的唯一标识。比如执行 insertSubView:atIndex:、removeFromSuperView 等方法时,我们也无法得到唯一标识,即使只截取部分路径也无法保证后期代码更新时不会动到这个部分。就算是运行时视图层级不会修改,以后需求迭代页面更新频繁的话,视图唯一标识也需要同步的更新维护。
7、这种问题就不好解决了,事件唯一标识的准确性难以保障,这也是通过运行时方法替换进行无侵入埋点很难在各个公司全面铺开的原因!!
8、总结:运行时替换方法进行无侵入埋点的方案。这套方案由于唯一标识难以维护和准确性难以保障的原因,很难被全面采用,一般都只是用于一些功能和视图稳定的地方,手动侵入式埋点方式依然占据大部分场景。无侵入埋点也是业界一大难题,目前还只是初级阶段,还有很长的路要走。我认为,运行时替换方法的方式也只是一种尝试,但是现实中业务代码太过复杂。同时,为了使无侵入的埋点能够覆盖得更全、准确度更高,代价往往是对埋点所需的标识维护成本不断增大。所以说,我觉得这种方案并不一定是未来的方向。
6、多线程的那些坑
1、第一个坑:常驻线程。尽量不要用!
如果你需要确实需要保活线程一段时间的话,可以选择使用 NSRunLoop 的另外两个方法 runUntilDate: 和 runMode:beforeDate,来指定线程的保活时长。让线程存活时间可预期,总比让线程常驻,至少在硬件资源利用率这点上要更加合理。
2、并发 是多线程技术的第二个大坑。
总结来讲,类似数据库这种需要频繁读写磁盘操作的任务,尽量使用【串行队列来管理】,避免因为多线程并发而出现内存问题。
3、内存问题
创建线程的过程,需要用到物理内存,CPU 也会消耗时间。而且,新建一个线程,系统还需要为这个进程空间分配一定的内存作为线程堆栈。堆栈大小是 4KB 的倍数。在 iOS 开发中,主线程堆栈大小是 1MB,新创建的子线程堆栈大小是 512KB。
除了内存开销外,线程创建得多了,CPU 在切换线程上下文时,还会更新寄存器,更新寄存器的时候需要寻址,而寻址的过程还会有较大的 CPU 消耗。所以,线程过多时内存和 CPU 都会有大量的消耗,从而导致 App 整体性能降低,使得用户体验变成差。CPU 和内存的使用超出系统限制时,甚至会造成系统强杀。这种情况对用户和 App 的伤害就更大了。
总结:其实,线程是个非常大的这个话题,涉及的知识也非常多,而这次只是选取了常驻线程和并发详细展开。因为,这两个技术非常容易使用不当,造成不堪设想的后果。
所以,建议是:【常驻线程一定不要滥用,最好不用】。对于多线程并发也是一样,【除非是并发数量少且可控,或者 必须要在短时间内快速处理数据的情况】,否则我们在一般情况下【为避免数量不可控的并发处理,都需要把并行队列改成串行队列来处理】。
7、耗电优化
获取耗电量1、获取电量?
在 iOS 中,IOKit framework 是专门用于跟硬件或内核服务通信的。所以,我们可以通过 IOKit framework 来获取硬件信息,进而获取到电量消耗信息。在使用 IOKit framework 时,
你需要:首先,把 IOPowerSources.h、IOPSKeys.h 和 IOKit 这三个文件导入到工程中;
然后,把 batteryMonitoringEnabled 置为 true;
最后,通过如下代码获取 1% 精确度的电量信息。
找到耗电线程 多线程 CPU 使用率检查的完整代码2、如何诊断电量问题?
回到最开始的问题,当你用排除法将所有功能注释掉后,如果还有问题,那么这个耗电一定是由其他线程引起的。创建这个耗电线程的地方可能是在其他地方,比如是由第三方库引起,或者是公司其他团队开发的库。
3、优化电量
对 CPU 的使用要精打细算,要避免让 CPU 做多余的事情。对于大量数据的复杂计算,应该把数据传到服务器去处理,如果必须要在 App 内处理复杂数据计算,【可以通过 GCD 的 dispatch_block_create_with_qos_class 方法指定队列的 Qos 为 QOS_CLASS_UTILITY】,将计算工作放到这个队列的 block 里。在 QOS_CLASS_UTILITY 这种 Qos 模式下,系统针对大量数据的计算,以及复杂数据处理专门做了电量优化。
4、I / O操作优化:
除了 CPU,I/O 操作也是耗电大户。任何的 I/O 操作,都会破坏掉低功耗状态。那么,针对 I/O 操作要怎么优化呢?
业内的普遍做法是,【将碎片化的数据磁盘存储操作延后,「先在内存中聚合」,然后再进行磁盘存储】(请求到的数据不要立即进行磁盘写入,先放入内存中,然后批量写入磁盘。)。碎片化的数据进行聚合,在内存中进行存储的机制,可以使用系统自带的 NSCache 来完成。
NSCache 是线程安全的,NSCache 会在到达预设缓存空间值时清理缓存,这时会触发 cache:willEvictObject: 方法的回调,在这个回调里就可以对数据进行 I/O 操作,达到将聚合的数据 I/O 延后的目的。I/O 操作的次数减少了,对电量的消耗也就减少了。
5、苹果官方耗电优化方案:
苹果公司专门维护了一个电量优化指南 链接“Energy Efficiency Guide for iOS Apps”,分别从 CPU、设备唤醒、网络、图形、动画、视频、定位、加速度计、陀螺仪、磁力计、蓝牙等多方面因素提出了电量优化方面的建议。所以,当使用了苹果公司的电量优化指南里提到的功能时,严格按照指南里的最佳实践去做就能够保证这些功能不会引起不合理的电量消耗。同时,苹果公司在 2017 年 WWDC 的 Session 238 也分享了一个关于如何编写节能 App 的主题“Writing Energy Efficient Apps”链接。
8、RunLoop精讲
1、资料:
我建议你按照下面的顺序来学习 RunLoop 原理,坚持下来你就会对 RunLoop 的基础概念掌握得八九不离十了。首先,你可以看一下孙源的一个线下分享《RunLoop》链接,对 RunLoop 的整体有个了解。然后,你可以再看官方文档,全面详细地了解苹果公司设计的 RunLoop 机制,以及如何运用 RunLoop 来解决问题。最后,了解了 RunLoop 的机制和运用后,你需要深入了解 RunLoop 的实现,掌握 RunLoop 原理中的基础概念。ibireme 的一篇文章 《深入理解RunLoop》,结合着底层 CFRunLoop 的源码,对 RunLoop 机制进行了深入分析。
9、PromiseKit 异步事件链编程
1> Promise 对象会有三种状态,分别是 pending、fulfilled、rejected:pending 表示 Promise 对象当前正在等待异步事件处理中;fulfilled 指的是 Promise 对象当前处理的异步事件已经成功完成;rejected 表示 Promise 对象当前处理的异步事件没有成功。
2>Promise 对象还有两个重要的方法,分别是 then 和 catch。Promise 对象每次执行完 then 和 catch 方法后,这两个方法会返回先前的 Promise 对象,同时根据异步操作结果改变 Promise 对象的状态。
3>执行 then 方法后返回的 Promise 对象是 rejected 状态的话,程序会直接执行 catch 方法。then 方法执行的就是订阅操作,Promise 对象触发 then 方法就是事件总线中的发布操作,then 方法执行完返回 Promise 对象能够继续同步执行多个 then 方法,由此,实现了一个发布操作对应多个订阅事件。有了 Promise 对象后,整个异步发布和订阅操作都以同步操作的方式表现出来了。Promise 对象不仅能够避免回调层层嵌套,而且通过 Promise 的统一接口,使得事件总线的发布和订阅操作更加规范和易用。
4> 多次异步请求通过 Promise 的方法调用,看起来就像进行同步操作一样,顺序和逻辑也更加清晰了。使用 then 方法可以让异步操作一个接着一个地按顺序进行。如果异步操作 fetchUserInfo 失败,会返回一个状态是 rejected 的 Promise 对象,返回的这个 Promise 对象会跳过后面所有的 then 方法直接执行 catch 方法。这就和事件总线中发布事件触发后,订阅事件会一个接一个执行是一样的。
5>PromiseKit 还为苹果的 API 提供了扩展。这些扩展需要单独集成,你可以在PromiseKit 组织页面获取。目前大部分常用的 API 都有扩展,比如 UIKit、Foundation、CoreLocation、QuartzCore、CloudKit 等等,甚至还支持了第三方的框架 Alamofire。如果你觉得 PromiseKit 提供的扩展还不够,还想让你使用的第三方库也支持 Promises 的话,可以通过 PromiseKit 提供的扩展文档,或者直接查看已支持的第三方库(比如 Alamofire )的扩展实现,去学习如何让其他库也支持 Promises。
11、富文本渲染:(WebView 、 YYText)
webView加载富文本1、WebView
使用 WebView 显示文章只需要创建一个 UIWebView 对象,进行一些基本滚动相关的设置,然后读取 HTML 字符串就可以了
和 UIWebView 的 loadRequest 相比,UIWebView 通过 loadHTMLString 直接读取 HTML 代码,省去了网络请求的时间,展示的速度非常快。不过,HTML 里的图片资源还是需要通过网络请求来获取。所以,如果能够在文章展示之前就缓存下图片,那么无需等待,就能够快速完整地展示丰富的文章内容了。那么,我应该使用什么方案来缓存文章中的图片呢?
在 Cocoa 层使用 NSURLProtocol 可以拦截所有 HTTP 的请求,因此我可以利用 NSURLProtocol 来缓存文章中的图片。
13、开发代码规范 (关于好的代码规范,接下来我会从常量、变量、属性、条件语句、循环语句、函数、类,以及分类这 8 个方面和你一一说明。)
1、常量:在常量的使用上,我建议你要尽量使用类型常量,而不是使用宏定义。比如,你要定义一个字符串常量, static NSString * const STMProjectName = @"GCDFetchFeed";
变量名应该可以明确体现出功能,最好再加上类型做后缀。这样也就明确了每个变量都是做什么的,而不是把一个变量当作不同的值用在不同的地方。
在使用之前,需要先对变量做初始化,并且初始化的地方离使用它的地方越近越好。
不要滥用全局变量,尽量少用它来传递值,通过参数传值可以减少功能模块间的耦合。
2、属性:在 iOS 开发中,关于属性的编码规范,需要针对开发语言做区分:Objective-C 里的属性,要尽量通过 get 方法来进行懒加载,以避免无用的内存占用和多余的计算。Swift 的计算属性如果是只读,可以省掉 get 子句。
3、类:在 Objective-C 中,类的头文件应该尽可能少地引入其他类的头文件。你可以通过 class 关键字来声明,然后在实现文件里引入需要的其他类的头文件。对于继承和遵循协议的情况,无法避免引入其他类的头文件,所以你在代码设计时还是要尽量减少继承,特别是继承关系太多时不利于代码的维护和修改,比如说修改父类时还需要考虑对所有子类的影响,如果评估不全,影响就难以控制。
4、首先,我觉得要利用好 Code Review 这个卡点,先使用静态检查工具对提交的代码进行一次全面检查。如果是 Swift 语言的话,你可以使用 SwiftLint工具来检查代码规范。Swift 通过 Hook Clang 和 SourceKit 中 AST 的回调来检查源代码,如何使用 SourceKit 开发工具可以参看这篇文章“Uncovering SourceKit”。
14、iOS学习资料(高级)
1、完整的例子来系统学习 App 开发,我推荐你查看一下 GitHub 上的Open-Source iOS Apps https://github.com/dkhamsing/open-source-ios-apps项目。作者在这个项目中收录了很多优秀的、完整的开源 iOS App,并做了详细分类,还专门标出了上架了 App Store 的开源 iOS App。
2、Awesome iOS https://github.com/vsouza/awesome-ios也是一个值得推荐的网站,里面包含了 iOS 开发的方方面面,而且内容都是经过人工筛选、分类的。我觉得,你遇到任何 iOS 的问题,都应该先到这个网站看看。Awesome iOS 最大的特点就是大而全,囊括了从开发、调试到发布 App Store 的各种学习资料,也包含了博客、书籍、教程、邮件订阅、播客的推荐。同时,这个网站还包括了 iOS 开发更细分的 Awesome 推荐,比如关于 ARKit 的 Awesome ARKit,关于面试问题收集的 Awesome iOS Interview question list 等等。
3、这里有份列表,列出了 iOS 领域那些知名开发者,你可以通过关注他们的博客、Twitter、GitHub ,来了解走在 iOS 领域前沿开发者的视野和 iOS 最新的动向。除了关注知名开发者外,你还可以关注下 开源项目团队的列表,如果你正在使用他们的开源项目,通过关注他们的动向,随时了解这些开源项目的最新进展。
4、Raywenderlich出版的图书质量都非常不错,可以一步一步教你掌握一些开发知识,内容非常实用,而且这些图书的涉及面广。比如,这些图书包括有 ARKit、Swift 服务端的 Vapor 和 Kitura、Metal、数据结构和算法的 Swift 版、设计模式、Core Data、iOS 动画、Apple 调试和逆向工程、RxSwift、Realm、2D 和 3D 游戏开发等各个方面。另外,objc.io家的图书会从原理和源代码实现的角度来讲解知识点,也非常不错,内容比 Raywenderlich 出版的图书更深入,适合有一定 iOS 开发经验的人阅读。Raywenderlich 和 objc.io 的书基本都是 Swift 来写的。如果你想更深入地理解 Objective-C 的话,我推荐《Objective-C 高级编程》这本书。这本书里的知识点并不多,主要讲的是内存管理、Block、GCD(Grand Central Dispatch)。这三个知识点对 Objective-C 来说非常重要,如果使用不当将会置你的工程于风险之中。正是因为涉及的知识点不多,所以全书能基于苹果公司公开的源码,集中讲清楚这三个知识点。这,非常难得。因此,如果你对内存管理、Block、GCD 了解地不是很透彻,我建议你仔细阅读这本书。如果你想要了解系统工作原理的话,我推荐阅读《程序员的自我修养 - 链接、装载与库》。这本书详细且深入地讲解了硬件、操作系统、线程的知识。阅读这本书之前,你需要先掌握 CPU、计算机原理、汇编、编译原理、C 语言、C++ 语言等计算机学科的基本知识。掌握了这些知识后再阅读这本书,它能帮你把知识串起来,帮你从代码编译、链接到运行时内存虚拟空间和物理空间映射的角度,了解一个程序从编写到运行时的底层知识的方方面面。现在编程技术不断推陈出新,不断通过添加中间层将底层埋住,新一代开发人员也越来越不重视底层知识,所以当他们学到的上层知识被更新替代以后就会感叹赶不上技术更新的脚步,知识焦虑感越来越严重。而读完这本书,你就会发现,有些知识是不会变的,不管上层如何变化,只要抓住这些知识就能够抓住核心,掌握技术的走向。《程序员的自我修养 - 链接、装载与库》耗时 30 年才被出版,期间作者不断优化其中的内容,最终成为一本经典图书。正如其名,程序员的自我修养。
15、iOS内存管理
1> 虚拟内存
由于要解决多程序多任务同时运行的这些问题,所以增加了一个中间层来间接访问物理内存,这个中间层就是虚拟内存。虚拟内存通过映射,可以将虚拟地址转化成物理地址。虚拟内存会给每个程序创建一个单独的执行环境,也就是一个独立的虚拟空间,这样每个程序就只能访问自己的地址空间(Address Space),程序与程序间也就能被安全地隔离开了。
有了虚拟内存这样一个中间层,极大地节省了物理内存。iOS 的共享库就是利用了这一点,只占用一份物理内存,却能够在不同应用的多份虚拟内存中,去使用同一份共享库的物理内存。
2> 对于在 iOS 开发过程中如何优化内存,苹果公司在 2018 年的 WWDC Session 416: iOS Memory Deep Dive https://developer.apple.com/videos/play/wwdc2018/416/上进行了详细讲解,其中就包含了 iOS 虚拟内存机制的变化。
图片资源不仅是影响 App 包大小的重要因素,也是内存的消耗大户。苹果公司在 2018 年的 WWDC Session 219: Images and Graphics Best Practices https://developer.apple.com/videos/play/wwdc2018/219/ 中,还专门介绍了关于图片的最佳实践,并针对减少内存消耗进行了详细讲解。
对于 App 处在低内存时如何处理,你可以看看这篇文章“No pressure, Mon! Handling low memory conditions in iOS and Mavericks” http://newosxbook.com/articles/MemoryPressure.html。
第三方内存检测工具有 MLeaksFinder https://time.geekbang.org/column/article/98560、FBRetainCycleDetector https://github.com/facebook/FBRetainCycleDetector、OOMDetector https://github.com/Tencent/OOMDetector。