Algorithm

LeetCode 9: 回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

思路

采用反转一半数字的思路,需要注意的是何时可以判断我们已经反转了一半数字:若为回文数,则反转数不断增加,原数不断减少,当两者相等(偶数)或反转数大于原数时(奇数),即为一半。

解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
func isPalindrome(_ x: Int) -> Bool {
if x < 0 || (x % 10 == 0 && x != 0) {
return false
}
var tmpX = x
var reverted = 0
while (tmpX > reverted) {
reverted = reverted * 10 + tmpX % 10
tmpX /= 10
}

return tmpX == reverted/10 || tmpX == reverted
}
}

Review

Advancements in the Objective-C runtime

这个 session 来自 WWDC2020 ,介绍了苹果对 OC Runtime 的一些优化。

调整类的结构

以往的类结构中,将只读的类信息结构体(编译器创建)存入 clean memory,以节约内存,而可读写的类信息结构体(运行时创建)存入 dirty memory ,由于不能 page out 而更为昂贵。本次的优化思路是将原来的可读写类信息结构体class_rw_t拆分为class_rw_t+class_rw_ext_t,后者即为该结构体中的动态部分,必须存入 dirty memory ,而剩下的class_rw_t则可以放入 clean memory。

经过优化后的类可达到 90% 的 clean memory ,系统层面节约 14 MB 内存。

head xxxxx | egrep ‘class_rw|COUNT’可查看应用class_rw_t消耗的内存,xxxx 为应用名。

相对方法地址

在类的结构体中包含一个方法列表,每个方法包含三个部分:selector/方法类型编码/IMP 指针,在 64 位的系统中,每个部分占用 8 个字节。

在 APP 的进程内存空间中,包括了各种链接库,这些库在链接过程中会将地址偏移以确定最终的地址,但由于方法实现地址不会脱离当前库的地址范围,方法列表并不需要 64 位的寻址空间,只需要在自己的库地址中查找引用函数地址,因此可以使用 32 位相对偏移来代替绝对 64 位地址。

这样做的优点有:

  1. 库被加载后不需要修正指针地址,因为偏移量是相同的
  2. 方法可被保存到只读存储器中,这会更加安全
  3. 使用 32 位+偏移量在 64 位平台上节约了一半内存

而针对 Method Swizzling 的情况,需要额外维护一个映射表以保存方法实现的地址。因此相较于优化前,Method Swizzling 的代价变大了。

Tagged Pointer 格式变化

优化前的 Tagged Pointer 会将标识位(是否为 Tagged Pointer)存在指针地址的最低位,之后的三位用来标识数据类型,包含 7 种情况。其中的OBJC_TAG_7类型还会将接下来的后 8 位用于记录拓展类型,以支持如UIColor之类的对象。

本次优化在 ARM 中将上述标识位的位置做了调整,将最高位作为 Tagged Pointer 的标识位,拓展类型位于之后的 8 位,而数据类型标识放在最低三位。

这样的调整会对objc_msgSend有一定优化,因为 msgSend 的会判断指针类型,包括普通指针、 Tagged Pointer 或 nil 。将标识位放在最高位可以一次判断出是否为后两者,而不用分别检查,节省了条件分支。

Tip

记录一下 Rx 中的一个常用操作符flatMap

该操作符的意思是将源Observable的元素应用一个转换方法,将其转换为Observables,然后将这些Observables的元素合并(flat)之后发送出来。

Share

带你打造一套 APM 监控系统

本篇文章比较全面的介绍了 iOS 端重要的几个性能监控的指标及其原理和方案,是一篇不错的总结。