本月重新读了一遍《剑指Offer》,并把题目做了一遍。
本月在swift上学习了一些很好用的技巧,比如 map
,filter
,reduce
。本月重点学习了 Runloop
, Runtime
, block
的底层细节,并且总结了一个 Method Swizzling 标准代码。
Week 27
July 02 to July 08
Algorithm:
[Leetcode - Medium] 240. Search a 2D Matrix II
1 | func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { |
Review:
《Adopting Modern Objective-C》
instancetype
用 instancetype
代替 id
能够提高类型安全,考虑如下代码
1 | + (instancetype)factoryMethodA; |
因为 instancetype
返回 +factoryMethodA
的类型 MyObject *
, 因为 MyObject
没有 -count
方法, 所以编译器会报错:1
main.m: ’MyObject’ may not respond to ‘count’
而 id 可以是任何类,编译器不会警告。
Properties
用 @property 定义变量,可以自动生成setter和getter方法
Enumeration Macros
用 NS_ENUM 和 NS_OPTIONS 代替 enum ,普通枚举用 NS_ENUM ,位枚举用 NS_OPTIONS 。
Object Initialization
在类中定义 designated initializers 并且用 NS_DESIGNATED_INITIALIZER 标记. 比如:1
- (instancetype)init NS_DESIGNATED_INITIALIZER;
Automatic Reference Counting (ARC)
推荐使用ARC。
Tip:
Off-Screen Rendering
意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。opengl
里离屏渲染会单独在内存中创建一个屏幕外缓冲区并进行渲染,而屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的。- 圆角效果:圆角效果的优化老生常谈,产生性能问题的根源在于原生圆角效果带来的离屏渲染开销。通常我们推荐直接使用圆角的素材,或者提前在子线程将图片进行圆角裁剪,这两者原理相同。除此之外,还有一种思路是在需要圆角的视图最上层添加一个中空的圆角遮罩层,以此来做出圆角效果。这个遮罩层和被盖在下面的视图在显示时会由 GPU 进行图层混合,而图层混合的开销远小于离屏渲染。值得一提的是,由于圆角效果通常在一屏中频繁出现,所以这个遮罩的图片素材可以只加载一次,并且应用于每一个圆角视图,避免重复加载。
- 阴影效果:值得注意的是系统原生的阴影实现要求
layer
的masksToBounds
值为 YES,所以原生的阴影效果和圆角是不兼容的。高效的阴影实现是为阴影指定shadowPath
,如果你还没用的话,不妨试一下。 - 适时替换轻量控件:在合适的时候用
CALayer
替换UIView
,这确实有效,不过盲目替换往往会造成代码维护的困难。这里举两个适合的场景:绘制线条时,完全可以替换。以及静态展示图片时,将图片对象赋值给 layer 的 content 属性,也完全可以达到效果。 - 图片解码:对于不同的图片格式,不同的解码算法,或者使用系统解码方法时的不同参数设置,都会影响解码性能,如果有这方面瓶颈的,不妨做多种尝试。
再说一个经典的例子:为了实现一个简单的画板需求,有人会在 UIView 上频繁调用 drawRect
方法进行新笔划的绘制,殊不知有一个天生的专用图层对象 CAShapeLayer
是很适合做这件事的。CAShapeLayer
不需要像普通 CALayer
一样创建寄宿图,不会造成巨量内存的使用,并且它使用了硬件加速。
UI 性能优化时,我们常常需要实时监测帧率。帧率监测工具 YYFPSLabel
的实现原理:使用 CADisplayLink
,在每帧的回调事件中,计数器 c 加一,并且累计时间间隔 t 也进行更新。当时间间隔够 1 秒后,使用 c/t 计算出过去 1 秒的帧率,而后计数器清零,时间戳更新为当前时间戳,再重复之前步骤。因此 YYFPSLabel
的帧率更新周期在 1 秒左右。
Share:
Swift 内部参数名 和 外部参数名
1 | // 不加任何参数名,直接写参数值 |
默认值
- 如果使用默认值,调用的时候,默认值对应的参数必须写参数名。这里影响的主要是函数和方法调用,因为类初始化本来就要写全参数名。
- 如果使用默认值并且默认值不是出现在最后,那调用的时候必须写全所有参数。
强制指定参数名
如果你想强制要求调用时必须加参数名,可以在声明的时候给参数加上外部参数名:
1 | func test(outName name: String, outAge age: Int) { |
如果外部参数名和内部参数名一样,可以直接在参数名前加#
:
1 | func test(#name: String, #age: Int) { |
强制取消参数名
对于需要参数名的函数,你也可以在参数名前加_
来强制取消参数名:
1 | class Test { |
Week 28
July 09 to July 15
Algorithm:
[Leetcode - Medium] 113. Path Sum II
1 | func pathSum(_ root: TreeNode?, _ sum: Int) -> [[Int]] { |
Review:
《What’s Next for Mobile at Airbnb》
本文介绍Airbnb决定放弃React Native
后的下一步工作。
主要是提出了一个新的框架MvRx
,新框架借鉴了很多React Native
的特性,同时使原生程序在build时可以加快速度。
Tip:
ABI 及 ABI稳定性
ABI是Application Binary Interface
的缩写,它是一个规范,通过这个规范,所有被独立编译的二进制实体才能被链接在一起并执行。这些二进制实体必须在一些很低层的细节上达成一致,例如:如何调用函数,如何在内存中表示数据甚至是如何存储以及访问metadata。ABI是平台相关的,因为它关注的这些底层细节会受到不同的硬件架构以及操作系统的的影响。
ABI稳定是指把ABI锁定在某种形式,以至于未来的编译器都可以生成遵从这种形式的二进制实体。
ABI的稳定性仅会影响到外部可见的公共接口和符号的不变性。而内部使用的符号、调用约定以及内存格局仍旧可以修改而不会破坏ABI约定。
import 引号与尖括号
一句话概括:带尖括号的语句是用来导入系统文件的,而带引号的语句则说明导入的是项目本地的头文件。
双引号是用于本地的头文件,需要指定相对路径,尖括号是全局的引用,其路径由编译器提供,如引用系统的库。cocoapod中引入的第三方库是作为系统库的,需要使用尖括号。
Share:
Tagged Pointer
为了节省内存和提高执行效率,苹果提出了Tagged Pointer
的概念。对于64位程序,引入Tagged Pointer
后,相关逻辑能减少一半的内存占用,以及3倍的访问速度提升,100倍的创建、销毁速度提升。
将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。
特点:
Tagged Pointer
专门用来存储小的对象,例如 NSNumber 和 NSDateTagged Pointer
指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要 malloc 和 free 。- 在内存读取上有着3倍的效率,创建时比以前快106倍。
Tagged Pointer
的引入也带来了问题,即Tagged Pointer
因为并不是真正的对象,而是一个伪对象,所有对象都有 isa 指针,而Tagged Pointer
其实是没有的。
Week 29
July 16 to July 22
Algorithm:
[Leetcode - Medium] 179. Largest Number
1 | func largestNumber(_ nums: [Int]) -> String { |
Review:
iOS — Identifying Memory Leaks using the Xcode Memory Graph Debugger
Xcode memory graph debugger可以帮助找到和修复循环引用与内存泄露。当被激活时,会暂停app运行,展现当前堆中的对象,对象的关系,对象间的引用。
Tip:
Swift Guide to Map Filter Reduce
Map
Use map
to loop over a collection and apply the same operation to each element in the collection.
Filter
Use filter
to loop over a collection and return an Array containing only those elements that match an include condition.
Reduce
Use reduce
to combine all items in a collection to create a single new value.
Share:
UITableView style
设置Plain
与Grouped
的区别
SectionHeader
和SectionFooter
在Plain
时是悬停的,在Grouped
不悬停Grouped
样式的SectionHeader
是自带间隔的,如果要取消间隔需要在相关代理方法中减去
Week 30
July 23 to July 29
Algorithm:
[Leetcode - Medium] 109. Convert Sorted List to Binary Search Tree
1 | func sortedListToBST(_ head: ListNode?) -> TreeNode? { |
Review:
mock
,通过制造模拟真实对象行为的假对象,来对程序功能进行测试或调试。
从设计思路上来说,interface
是抽象出一套行为接口或者属性,且并不关心实现者是否存在具体实现上的差异。而 mock
需要模拟对象和真实对象两者具有相同的行为和属性,以及一致的行为实现。interface
更适用于模块解耦、功能扩展相关的工作;而 mock
更多的应用在调试、测试等方面的工作。
mock
并不是一种特定的操作或者编程手段,它更像是一种剖析工程细节来解决特殊环境下难题的解决思路。无论如何,如果我们想要继续在开发领域上继续深入,必然要学会从更多的角度和使用更多的工具来理解和解决开发上的难题,而 mock
绝对是一种值得学习的开发思想
Tip:
NSDecimalNumber
, An object for representing and performing arithmetic on base-10 numbers.
可以方便地处理货币相关的计算,包括加减乘除等。可以通过 NSDecimalNumberHandler
对运算的处理策略进行设置,比如四舍五入,数据溢出,除零等异常。
Share:
Method Swizzling
注意事项及代码示例
Swizzling
应该总在+load
中执行。+load
会在类初始加载时调用,+initialize
方法是以懒加载的方式被调用的。Swizzling
应该总是在dispatch_once
中执行Swizzling
在+load
中执行时,不要调用[super load]
1 |
|