前言
距离上一次写博客已经过去 9 年了。回头看了一下最后几篇文章的时间,确实有点恍惚,时光荏苒这句话放在这里还挺合适。
这几年技术变化很快,尤其随着 AI 时代的来临,查资料、整理脉络、校对文字、补齐上下文的成本都低了不少。以前写一篇文章,很多精力会花在资料整理和表达打磨上;现在这些环节可以被工具分担一部分,反而更应该把项目里的问题、判断和思考沉淀下来。
所以接下来还是想把博客重新写起来,并且尽量不间断地继续更新。在继续写新的内容之前,先把过去几年写过的文章做一次总结和索引,也算是给这个博客重新接上线。
之前写文章的时候基本都是遇到一个问题、做了一个控件、踩了一个坑,然后顺手整理出来。时间长了以后,单篇文章之间其实是有脉络的:有些是 iOS 工程结构,有些是 Runtime 和内存管理,有些是 UI 交互和开源控件,还有一些是读书笔记、翻译和服务端相关的小实践。本文就把它们按主题重新整理一下,方便以后自己回头查,也方便刚好搜到某一篇的同学顺着同一类问题继续看下去。
整体回顾
目前博客里的旧文章主要集中在 2014 到 2017 年,一共 42 篇。现在回头看,这些文章当然带着很明显的时间痕迹:Objective-C、Masonry、CocoaPods、早期 Swift、Runtime、反编译,这些都是当时做 iOS 开发绕不开的话题。
不过很多问题并没有过时,只是换了技术栈和工具形态。比如代码怎么拆、接口怎么设计、复杂页面怎么优化、遇到奇怪问题怎么验证,这些到现在依然是工程里每天都会遇到的事情。
大致按年份看:
- 2014 年,主要是刚开始写 Blog,以及一个 Android 开源控件。
- 2015 年,文章最多,覆盖了 iOS 项目结构、Objective-C 基础、UITableView 性能、GCD、Auto Layout、Runtime、Swift 控件等。
- 2016 年,更多是在总结工程化、模块化、代码组织、API 设计,也有一些更底层的内存管理和消息发送内容。
- 2017 年,主要集中在 Runtime、反编译、Xcode Bug 分析,以及几个控件和工具库。
如果按阅读路线来分,则可以这样看:
- 想看 iOS 工程实践,可以先看项目结构、模块化、代码组织。
- 想看 Objective-C 底层,可以从
@autoreleasepool、__autoreleasing、objc_msgSend、methodSignatureForSelector:这几篇开始。 - 想看 UI 实现,可以看 Auto Layout 系列、UITextView 图文混排、几个 TTG 开源控件。
- 想查具体问题,就直接按下面的主题索引找。
工程结构、架构与协作
这一类文章主要讨论“代码应该怎么放、怎么拆、怎么协作”。很多观点放到现在不一定是唯一答案,但是问题本身一直存在:项目变大以后,如何让代码还能被理解、修改和复用。
iOS项目的目录结构-原创
比较早的一篇工程结构整理,重点是从目录层面把业务、工具、资源、第三方代码分清楚。Entity和Model的不同-关于代码的数据层
讨论数据层对象的职责边界,适合和后面的 ViewModel、数据加工、接口返回设计一起看。利用NSProxy实现消息转发-模块化的网络接口层设计-原创
从NSProxy和消息转发切入,把网络接口层做成更清晰的调用入口。对组件化与模块化的思考与总结
当时读了不少组件化文章后做的一次整理,重点是“模块”和“组件”的区别,以及如何从业务、技术、界面、数据几条线去拆分。总结一些iOS项目中组织代码的方法
从 CocoaPods 私有库、Carthage、Xcode Group、Category、#pragma mark等不同粒度整理代码组织方法。API返回结果设计经验与总结
虽然分类放在服务端,但本质也是客户端和服务端协作的接口契约问题。一次审核被拒的经历-关于iCloud到底应该备份什么数据
一个偏实践的审核问题,记录了 iCloud 备份数据边界,属于踩坑型文章。
Objective-C、Runtime 与内存管理
这一组文章是我自己回头看时比较有连续性的部分:一开始是内存和属性行为,后面慢慢过渡到 Runtime、消息转发、反编译和系统实现。
NSString的Copy与内存分配
从NSString、NSMutableString的copy行为入手,看对象拷贝和内存分配。@autoreleasepool-内存的分配与释放
整理@autoreleasepool的基本行为,是后面看__autoreleasing的基础。结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing
通过 Out Parameters 的 crash 例子,继续看__autoreleasing和 autorelease pool 的关系。翻译-为什么objc_msgSend必须用汇编实现
一篇翻译文章,但对理解 Objective-C 消息发送、寄存器、调用约定很有帮助。用Runtime的手段填充任意NSObject对象的nil属性
一个用 Runtime 遍历属性并填充默认值的小实践,适合理解属性列表和动态处理。解决iOS项目的版本兼容问题-结合宏、Category和Runtime
用宏、Category、Runtime 做版本兼容处理,属于工程实践和 Runtime 的交叉。在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper
围绕对象销毁阶段执行任务,涉及生命周期、关联对象和封装方式。反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash
一个比较典型的“写 Demo 验证 -> 反编译分析 -> 总结原因”的问题排查过程。反编译分析并模拟实现methodSignatureForSelector方法
从methodSignatureForSelector:的行为差异出发,最后尝试模拟实现,适合作为 Runtime 深入阅读的一篇。
并发、性能与小技巧
这一类文章更偏“项目里马上能用到”的经验。现在很多技术栈已经变化了,但里面的一些判断方式仍然有参考意义,比如提前计算、缓存、减少重复创建、明确线程上下文等。
提升UITableView性能-复杂页面的优化
从复杂动态页面入手,总结 Cell 复用、高度缓存、数据预处理、View 缓存等优化点。GCD使用经验与技巧浅谈
对 GCD 常见使用方式做了一次整理,包括队列、同步异步、延迟执行等。为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
一个更细的问题:如何给 GCD 队列绑定 Objective-C 对象,并正确处理内存管理权。Block类型变量-缓存Http请求与回调
通过 Block 保存回调,解决重复请求和回调分发问题。有关宏定义的经验与技巧-简化代码-增强Log
记录了一些宏定义用法,重点是让重复代码更少、调试信息更清楚。
UI、布局与交互实现
这一组基本都是“先有一个效果,再拆解实现”的文章。相比纯理论,我自己更喜欢这种写法:先把画面、交互、约束关系摆出来,再一点点推到代码。
UITextView编辑时插入自定义表情-简单的图文混编
记录UITextView中插入自定义表情的基础实现。UITextView编辑时插入自定义表情-续-自定义表情图片的大小
继续处理NSTextAttachment图片大小的问题,是上一篇的补充。有趣的Autolayout示例-Masonry实现
Auto Layout + Masonry 系列第一篇,用有趣的布局示例解释约束。有趣的Autolayout示例2-Masonry实现
Auto Layout 系列第二篇,继续通过示例练习约束关系。有趣的Autolayout示例3-Masonry实现
Auto Layout 系列第三篇。有趣的Autolayout示例4-Masonry实现
Auto Layout 系列第四篇。有趣的Autolayout示例5-Masonry实现
Auto Layout 系列第五篇,也是这个系列后面的一次补充。用QuartzCode快速实现一个收藏动画
通过 QuartzCode 快速做收藏动画,重点是工具生成动画代码后的理解和使用。
开源项目与控件实现
这些文章基本都围绕一个具体控件或工具展开:为什么做、最终效果是什么、API 怎么设计、关键实现点在哪里。现在看起来,有些库可能已经偏老了,不过当时做开源项目时对封装、文档、Demo 的训练还是很有价值。
Android开源库-LinkTextView-原创
早期的 Android 开源控件,用来处理文本中的链接点击。Swift写的库-TTGSnackbar
一个 Swift 写的 Snackbar 控件,重点在交互展示和 API 封装。Swift开源项目: TTGEmojiRate的实现
基于UIBezierPath、手势和绘制实现 Emoji 评分控件。开源项目-TTGTagCollectionView
标签布局控件,适合看布局计算、数据源和代理 API 的设计。Swift开源Mac App - BingWallPaper
一个 macOS 菜单栏应用,用 Swift 获取 Bing 壁纸并设置桌面背景。开源项目-拼图验证控件TTGPuzzleVerify的实现
拼图验证控件,包含图片裁剪、遮罩、滑动验证等实现点。在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper
既是开源库,也可以归到 Runtime 和对象生命周期相关内容里。
读书笔记与翻译
这部分主要是阅读《Effective Objective-C》和技术文章后的整理。它们不算完整翻译,更像是把书里的点和自己的理解合在一起做记录。
Effective-Objective-C-读书笔记-Item-1
Effective Objective-C 系列笔记开头。Effective-Objective-C-读书笔记-Item-2
继续整理 Objective-C 基础概念。Effective-Objective-C-读书笔记-Item-4-如何正确定义常量
重点是常量定义方式,也是实际项目里很常见的问题。Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5
整理枚举的正确使用方式,包括位掩码等内容。翻译-为什么objc_msgSend必须用汇编实现
这篇同时也属于 Runtime 主题,放在这里是因为它本身是翻译文章。
服务端、跨端与其它
虽然博客大部分是 iOS,但也有几篇不是纯 iOS 的内容。
RPC框架Thrift例子-PHP调用C++后端程序
用一个小例子记录 Thrift、PHP、C++ 后端之间的调用。API返回结果设计经验与总结
从接口返回结构出发,讨论code、message、data等字段的设计。Android开源库-LinkTextView-原创
Android 控件文章,也算是早期跨端尝试的一部分。第一篇Blog
真正意义上的第一篇,更多是一个开始的记录。
按年份索引
如果不按主题,而是按时间看,脉络会更明显一些。
2014
2015
- iOS项目的目录结构-原创
- Effective-Objective-C-读书笔记-Item-1
- Effective-Objective-C-读书笔记-Item-2
- Effective-Objective-C-读书笔记-Item-3
- 利用NSProxy实现消息转发-模块化的网络接口层设计-原创
- NSString的Copy与内存分配
- 提升UITableView性能-复杂页面的优化
- Block类型变量-缓存Http请求与回调
- 一次审核被拒的经历-关于iCloud到底应该备份什么数据
- Entity和Model的不同-关于代码的数据层
- UITextView编辑时插入自定义表情-简单的图文混编
- Effective-Objective-C-读书笔记-Item-4-如何正确定义常量
- 有关宏定义的经验与技巧-简化代码-增强Log
- @autoreleasepool-内存的分配与释放
- Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5
- 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
- GCD使用经验与技巧浅谈
- RPC框架Thrift例子-PHP调用C++后端程序
- UITextView编辑时插入自定义表情-续-自定义表情图片的大小
- 有趣的Autolayout示例-Masonry实现
- 用Runtime的手段填充任意NSObject对象的nil属性
- 解决iOS项目的版本兼容问题-结合宏、Category和Runtime
- 有趣的Autolayout示例2-Masonry实现
- Swift写的库-TTGSnackbar
- Swift开源项目: TTGEmojiRate的实现
- 有趣的Autolayout示例3-Masonry实现
- 开源项目-TTGTagCollectionView
2016
- 对组件化与模块化的思考与总结
- 总结一些iOS项目中组织代码的方法
- 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing
- API返回结果设计经验与总结
- 翻译-为什么objc_msgSend必须用汇编实现
- 有趣的Autolayout示例4-Masonry实现
- Swift开源Mac App - BingWallPaper
- 开源项目-拼图验证控件TTGPuzzleVerify的实现
2017
- 用QuartzCode快速实现一个收藏动画
- 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper
- 有趣的Autolayout示例5-Masonry实现
- 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash
- 反编译分析并模拟实现methodSignatureForSelector方法
总结
这次整理之后再看,过去这些文章其实大致围绕几个关键词:工程结构、Objective-C 底层、UI 实现、性能优化、开源封装。
单篇文章写的时候往往只是解决一个具体问题,但放在一起看,就能看到当时学习和做项目的路径:先解决页面和业务问题,再关注代码组织和模块拆分,最后慢慢深入到 Runtime、内存管理和反编译验证。
隔了 9 年再重新开始写博客,感觉像是把一个很久没打开的项目重新拉起来。代码可以重新编译,文章也可以继续更新。AI 工具会让整理和表达的成本更低,但真正值得记录的,还是项目里遇到的问题、做判断时的取舍,以及过一段时间后依然能回头看的经验。
后面新的文章也会沿着这个索引继续补充。这样博客就不只是按时间堆在一起,而是能变成一份可以回头查、也可以顺着阅读的技术笔记了。