iOS 摸鱼周报

iOS 摸鱼周报

本期概要

  • 话题:Apple 在辅助功能上持续创新;IAP 自动续订提价通知更新
  • 面试模块:学习 OOMDetector 中的 CRC64 应用实践
  • 优秀博客:iOS 内存
  • 学习资料:一份英语进阶指南
  • 开发工具:一款 macOS 上的 纯文本编辑器 CotEditor

本期话题

Apple 在辅助功能上的又一创新

@zhangferry:作为一款受众非常广的产品,针对特殊人群的辅助功能就显得尤为重要,不得不说 Apple 对辅助功能的重视程度和探索精神都是值得尊敬的。最近 Apple 又公布了一些基于软硬件和机器学习带来的辅助功能提升。

针对盲人和视力障碍的人群:Apple 基于配有 LiDAR 的设备可以探测到前方是否有门,门距离自己有多远,甚至要通过推还是拉的方式开门都能识别出来。

针对行动不便的人群:有一项 iPhone 结合 Apple Watch 的功能,借助于 Apple Watch 的 Mirroring 功能,可以用手机远程操作 Apple Watch。同时 Apple Watch 也有提升,通过 AssistiveTouch 技术,可以让 Apple Watch 识别特定手势,像是手指两次捏合的手势可以用于接电话、拍照、暂停音乐等。

针对听力障碍的人群:在 iPhone、iPad、Mac 配备了实时字幕功能,不只是针对 Facetime,对于任意音频内容,包括外部 App 都可以使用。样式是在设备顶部展示一个文本转义框,字体大小还可调整。

同时 VoiceOver 也进一步完善,增加了 20 多个地区语言的支持。

IAP 自动续订提价通知更新

@zhangferry:自动续订是 Apple Store 付费产品使用最广泛的一个订阅选项。当一个已经被用户续订的产品进行提价时,Apple 会通过邮件、推送和 App 内消息的形式告知用户,如果用户未选择接受变更价格,下个续订周期就会默认中断。这可能会导致部分用户的不理解,影响其体验。该项改进意在增加一些条件,使得提价之后的续订周期可以默认延续。这个条件是:每年提价不超过一次,同时订阅价格上调不超过 5 美元和 50%,或者年度订阅价格上调不超过 50 美元和 50%,并且是在法律允许的范围内。该举措仍会通知到用户价格的变更。

面试解析

整理编辑:Hello World

学习 OOMDetector 中的 CRC64 应用实践

OOMDetector 中对 CRC64 的应用讲解实际应用时的一些变体操作。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#define POLY64REV     0x95AC9329AC4BC9B5ULL
static uint64_t crc_table[8][256];

void init_crc_table_for_oom(void) {
uint64_t c;
int n, k;
static int first = 1;
if(first) {
first = 0;
// 针对单个字节值生成单表
for (n = 0; n < 256; n++) {
c = (uint64_t)n;
for (k = 0; k < 8; k++) {
// LSB 右移生成逻辑, 主要适用于小端模式
f (c & 1)
c = (c >> 1) ^ POLY64REV;
else
c >>= 1;
}
crc_table[0][n] = c;
}
// 生成不同权重的 CRC 值
for (n = 0; n < 256; n++) {
c = crc_table[0][n];
for (k = 1; k < 8; k++) {
c = crc_table[0][c & 0xff] ^ (c >> 8);
crc_table[k][n] = c;
}
}
}
}

uint64_t rapid_crc64(uint64_t crc, const char *buf, uint64_t len) {
register uint64_t *buf64 = (uint64_t *)buf;
register uint64_t c = crc;
register uint64_t length = len;
// 取反
c = ~c;
while (length >= 8) {
c ^= *buf64++;
// 根据不同权重的字节数据查表
c = crc_table[0][c & 0xff] ^ crc_table[1][(c >> 8) & 0xff] ^ \
crc_table[2][(c >> 16) & 0xff] ^ crc_table[3][(c >> 24) & 0xff] ^\
crc_table[4][(c >> 32) & 0xff] ^ crc_table[5][(c >> 40) & 0xff] ^\
crc_table[6][(c >> 48) & 0xff] ^ crc_table[7][(c >> 56) & 0xff];
length -= 8;
}
// 这里注释的内容,是单字节计算的逻辑,即每次计算一个字节,可能最早的 OOMDetector 采用的是该计算方式。
// buf = (char *)buf64;
// while (length > 0) {
// crc = (crc >> 8) ^ crc_table[0][(crc & 0xff) ^ *buf++];
// length--;
// }
// 取反
c = ~c;
return c;
}

主要有两步操作,CRC 生成表以及 CRC 查表。以这两步出发学习一下 CRC 实际应用中的变体以及目的。

生成表-多表级联

有别于《#53 周报》中的单表查询方式,OOMDetectorcrc_table定义为crc_table[8][256]二维矩阵的多表查询。其中单维度的表仍然以字节大小(8 bit)作为位宽生成,即单个表大小为 2 ^ 8 = 256crc_table[8]表示不同权重的单表, 这种方式称为 CRC 位域多表查询 。

CRC 位域多表查表方法与传统的 CRC 查表方法最大的不同在于多表级联压缩表格空间。

如果是传统单表查询,一次性查询双字节数据的 CRC,需要单表大小为 256 * 256,采用多表级联只需要 2 *256,实现了极大的空间压缩。

更详细的原理可以参考《CRC位域多表查表方法》,这里只理解该优化依赖的核心性质:crc_table[A ^ B] = crc_table[A] ^ crc_table[B]

从 CRC 计算的本质出发,其实就是依次的计算每一 bit 位的余数,而余数的结果值,只和 POLY计算的次数和顺序有关。

我们以示例 0xBC来拆解这个计算过程:

  1. 采用右移的计算方式,左移和右移的区别在下小结中讲解。1011 1010(0xBA) 计算 CRC Table 简化为:

    1
    1011 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7)
  2. 现在 0xBA 根据异或性质分解为 0xB0 ^ 0x0A

  3. 我们单独计算 0xB00x0A的 CRC 值,来看他们的计算过程

    1
    2
    3
    4
    5
    0xB0 = 0b 1011 0000;
    CRC[0xB0] = 1011 0000 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ 0>>4 ^ 0>>5 ^ 0>>6 ^ 0>>7);

    0x0A = 0b 0000 1010;
    CRC[0x0A] = 0000 1010 ^ (0 ^ 0>>1 ^ 0>>2 ^ 0>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7)
  4. 上面两个公式做异或,最终结果值和 CRC[0xBA]相等

    1
    2
    3
    4
    CRC[0xB0] ^ CRC[0x0A] = 1011 0000 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ 0>>4 ^ 0>>5 ^ 0>>6 ^ 0>>7) ^ 0000 1010 ^ (0 ^ 0>>1 ^ 0>>2 ^ 0>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7);

    // 由于 0 异或任何值还是原值,结果可以简化为:
    1011 0000 ^ 0000 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7);
  5. 即证明 CRC 性质:crc_table[A ^ B] = crc_table[A] ^ crc_table[B]成立。

CRC 级联查表另一个需要解决的就是多字节数据的权重问题,上面证明了 CRC 性质可行性,但是在查表时为了方便,使用的索引并非是直接拆解的数据,例如 CRC[0x AA BB] = CRC[0x AA 00] ^ CRC[0xBB] ,两次查表索引分别为 0xBB 和0xAA,并非是 0xBB 和 0xAA 00

权重就是指的由 CRC[0xAA] 计算 CRC[0xAA 00] ... CRC[0xAA 00 00 00 00 00 00 00] 等数据,实现也很简单,可以看做是已知单表crc_table[256]的值,求数据的值,即 init_crc_table_for_oom()中第二个 for 的目的。

生成表-单表反向计算(reversed)

OOMDetector 生成单表时区别于传统的 MSB 左移(<<) 计算方式,采用的是 LSB 右移(>>) 生成方式。原因是 iOS 的主机字节序是小端模式,但是一般规范中要求数据在网络传输过程中采用网络字节序(大端模式)。

一个系统中针对每一个字节内的 bit 位也是有顺序的,称为位序。

位序一般和主机字节序是一致的,例如一个数据 0x11 22 在 iOS 内存中的存储为 0x22 0x11,实际 0x11 = 0b 0001 0001的存储顺序也是逆序的,表示为 0b 1000 1000

为了按照网络字节序传输规范作为计算 CRC 顺序的依据,小端的机器上在使用 CRC 时都采用右移计算,即 0b 1000 1000按照右移顺序依次计算 0 0 0 1 0 0 0 1,这样保证了规范性,无论其他 server 接收端是大端模式还是小端模式,在拿到数据后自己按照主机字节序重新计算即可。

反向计算最重要的一点:由于计算顺序反向,所以 POLY生成多项式的值相对于传统给定的生成多项式值,也要做位序的反向生成新的 POLY值。

查表

rapid_crc64()查表中一次计算了 8 字节数据的 CRC 值,根据crc_table[A ^ B] = crc_table[A] ^ crc_table[B]性质,查表操作以对应权重的字节数据在相应的级联表中查找值即可,具体到每一个级联表,和单字节的查表逻辑一致。最终结果是各个权重字节数据的异或结果。

示例计算数据为 0x AA BB CC DD 11 22 33 44 ,在小端模式是逆序存储的,所以在计算 CRC 值时是从低字节到高字节(即从右到左)顺序计算的。分解计算步骤来分析:

  1. 根据异或计算的性质 0x AA BB CC DD 11 22 33 44 = 0x44 ^ 0x33 00 ^ 0x 22 00 00 ^ 0x11 00 00 00 ... ^ 0xAA 00 00 00 00 00 00 0
  2. 结合 CRC 性质 CRC[0x AA BB CC DD 11 22 33 44] = CRC[0x44] ^ CRC [0x33 00] ^ .... CRC[0xAA 00 00 00 00 00 00 00]
  3. 根据二维级联表,查找每一个字节的 CRC,CRC[0x 3300] = crc_table[1][0x33]。注意这里是用 0x33作为索引计算数据 0x33 00的值,和计算数据 0x33 是有区别的,所以在生成表时需要做二次遍历以生成不同权重的单表值。(这里的权重可以理解为单字节数据在 8 个字节中的位置,从左到右为 MSB => LSB)

OOMDetector 在查表之前和查表之后都做了一次取反操作 c = ~c,该变体的目的是解决普通 CRC 无法区分只有起始 0 的个数不同的两个数据。(暂时未理解这个目的,所以直接引用 wiki 中的解释)

《循環冗餘校驗-wiki》:移位寄存器可以初始化成1而不是0。同样,在用算法处理之前,消息的最初n个数据位要取反。这是因为未经修改的CRC无法区分只有起始0的个数不同的两条消息。而经过这样的取反过程,CRC就可以正确地分辨这些消息了。

优秀博客

整理编辑:皮拉夫大王在此

本期博客主题:iOS 内存。如果你对以下几个问题不了解的话,推荐阅读本期的博客。

  • 什么是 MMU?什么是 clean/dirty/compressed memory?
  • 申请 malloc(1),malloc_size 是多少?
  • 小内存释放,内存会立即还给系统吗?
  • TCMalloc 主要解决什么问题?
  1. iOS Memory 内存详解 (长文) – 来自掘金:RickeyBoy

@皮拉夫大王:本文主要介绍了 iOS 内存相关的基础知识,可以帮助读者建立内存知识全景图。我们可以带着问题去阅读这篇文章:(1)、虚拟内存是如何映射到物理内存的?(2)、clean/dirty memory是如何区分的?一块 dirty memory 的单位大小是多少?

  1. 深入理解内存分配 – 来自网易数帆:阿凡达

@皮拉夫大王:内存分配的硬核文章,内容很有意思。通过阅读这篇文章,首先我们会了解 free 的过程,顺带也就能理解作者举的例子:str[0]=’a’ 报错非 bad_access 的原因了。另外作者列举了多种替换系统默认内存分配方式,这也是比较有意思的一点。

  1. Matrix-iOS 内存监控 – 来自腾讯云:微信终端团队

@皮拉夫大王:来自微信的 matrix 内存监控原理介绍工具,能够抓取每个对象生成时的堆栈。与 OOMDetector 的原理一致,但是性能上更胜一筹。如此大量且高频的堆栈抓取和保存,matrix 是如何做优化的?可以通过阅读本文来了解细节。

  1. TCMalloc解密 – 来自:Wallen’s Blog

@皮拉夫大王:对《深入理解内存分配》中提到的 TCMalloc 感兴趣的可以继续阅读这篇文章。

见闻

这一周阅读/浏览到的有趣的资讯。

1、Mac 与游戏无缘,M1 来了也没用 – 来自公众号:APPSO

@远恒之义:提到游戏,具体到 PC 端的游戏,Mac 电脑基本是沾不上边的。传统意义上的 PC 游戏,指的是在 Windows 电脑上玩的游戏,Mac 电脑只是一个生产力工具。我曾下载过战网客户端,在 Mac 上玩暴雪游戏《炉石传说》,但这样原生支持 Mac 平台的厂商并不多。我也用过腾讯 START 云游戏,对网络要求很高,在 MacBook 上玩《英雄联盟》,打团时的延迟尚能接受。最近拿 PS5 手柄在 iPad 上试玩 Arcade 游戏,游戏体验还不错,也能兼容 Mac 平台。那么,为什么 Mac 距离主流游戏市场这么远呢?M1 芯片的到来,能给 Mac 游戏带来新的机遇吗?作者在文中给出了答案。

2、对 iPod 说再见,我想带你走进无数人的「青春记忆」 – 来自少数派:宛潼

@远恒之义:停产了,售罄了,下架了,拥有 20 年寿命的 iPod,走到了生命的终点。作为一款音乐播放器,iPod 的产品线十分丰富。无论是初代经典 iPod Classic,还是短暂尝试的 iPod mini,还有被用户吐槽最多的 iPod shuffle,多次探索新形态、新功能和新技术的 iPod nano,功能强大的 iPod touch,这些都已成为了历史,让人怀恋。拥有过 iPod 的你,是否也有「爷青结」的感叹呢。就让本文的作者带你一起了解 iPod 相关的彩蛋产品,唤起你的「青春记忆」吧。

3、Bash tips: Colors and formatting (ANSI/VT100 Control sequences)

@zhangferry:终端常见的输出样式是黑白,但实际上它还可以设置颜色和一些简单的格式,这些样式的配置可以利用 ANSI 转义码。整个过程分为两步,第一,让 Bash 识别转义码,第二步,指定转义码颜色。看一个例子:

1
$ echo -e "\e[31mRed Text\e[0m"

这个命令输出内容是红色文本的 Red Text,参数含义说明如下:

Option Description
-e 开启反斜杠的转义功能
\e[ 它是 Bash 识别转义的起始标志符。\e 是 ASCII 码中的 ESC,表示控制符,8 进制表示为 \033,也是常见用法。[ 是转义序列开始标记符
31m 由 ANSI 转义码定义,31 表示红色,m 表示颜色取值结束
\e[0m \e 含义同上,开始识别 ANSI,0 表示重置设置

4、Airport

@zhangferry:TestFlight 是 Apple 用于提供内测功能的应用,一般我们只是用它测试自己的应用或者已安装应用的升级尝鲜。TestFlight 版本的 App 有这些优点:审核相比 AppStore 要松很多、功能限制少、对于需要内购的产品可以 0 元尝鲜。但是对于外界还有哪些不为人熟知的 TF 版应用我们是不清楚的,Airport 要做的事情就是这个,你可以在这里根据分类和搜索挑选你喜欢的应用参与测试。

5、大疆无人机模拟飞行

@zhangferry:这是大疆出的无人机模拟飞行体验网站,打开之后等待页面渲染完成就可以在一个虚拟城市里体验操纵无人机的感觉。该模拟还配备了视角切换、拍照、录像等物理机具备的所有几乎所有功能。同时还有物理撞击的模拟,也就是说如果你飞行中撞到了建筑物,无人机也是会坠毁的,第一视角的坠毁效果做的很不错。

学习资料

整理编辑:zhangferry

英语进阶指南

地址:https://babyyoung.gitbook.io/english-level-up-tips/

英语是程序员绕不过去的一项技能,虽然我们可能从小学就开始接触英语了,但直到毕业工作,英语能够不成为学习障碍还是一件不容易的事情。这其中的差别很大成分可以归结为学习方法,这份文档就是这样一个注重方法和可操作性的英语学习指南。

工具推荐

整理编辑:CoderStar

CotEditor

地址https://coteditor.com/

软件状态:免费

软件介绍

适用于 macOS 的纯文本编辑器,轻巧、整洁并且功能强大。

CotEditor

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS 摸鱼周报 #53 | 远程办公正在成为趋势

iOS 摸鱼周报 #52 | 如何规划个人发展

iOS 摸鱼周报 #51 | 游戏版号恢复发放

iOS 摸鱼周报 第五十期

iOS 摸鱼周报

iOS 摸鱼周报

本期概要

  • 话题:远程工作正在成为趋势
  • 面试模块:CRC 实践应用:理论推导
  • 优秀博客:App Extension
  • 学习资料:一个大学生的学习计划
  • 开发工具:适用于 macOS现代 媒体播放器,IINA 由开源媒体播放器 mpv 提供支持,几乎可以播放您拥有的所有媒体文件。

本期话题

远程办公正在成为趋势

随着疫情的影响,远程工作的趋势正在以更快的速度发展和普及。携程、爱彼迎、GitLab 等都先后出台政策鼓励员工远程办公,可以预见不久之后还会有更多公司支持常态化的远程办公。远程工作是一个很早就有的概念,它也天然适合互联网行业,那为什么却发展这么慢呢,可以简要对比下传统办公模式和远程办公各自的优缺点。

传统办公模式

优点是同事之间沟通便利、工作状态更沉浸、公司整体也更有凝聚力;如果公司福利好的话,像管饭、健身等都在增加其优势。

传统办公最大缺点就是通勤了,特别是离家远的话,每天两遍的通勤简直磨炼人的。因为必须到岗,个人的自由度,作息习惯也相应的受公司制度影响。

远程办公

最大的优点是赋予了个人很大的自由性。不仅省去了通勤,还能更顾家,像接送小孩等,也能很方便的排开时间去处理。自由度是否意味着幸福感可能还要因人而异,但这种模式确实把选择权交到了个人手里。同时其对公司也有很大好处,会节省很多管理成本。

缺点的话,从我的观察是远程办公会导致大家最多抱怨的是不知道吃什么😄。

还有一方面是它对效率的影响。这段时间北京疫情一直不稳定,我断断续续已经居家办公有一个月时间了。实际感受来看,在家办公的效率平均下来可能是在公司的 80% - 90%。这效率损失主要在两个地方,一是在家被琐事打扰的概率要比公司更高,二是投入度的影响,公司的环境所带来的沉浸感(大屏显示器)在家较难实现(也会因人而异)。

也是因为这些影响,不是所有人都习惯远程办公的,但这是由于强制远程带来的问题,那如果是否远程交到员工自己手里呢?可以再结合采取了远程办公的公司采取的制度来看这个事情。

公司 远程制度
携程 「3 + 2」模式,每周三、周五在家远程办公,其余三天去公司上班
爱彼迎 永久灵活选择,可在家、在办公室,无论地点在哪,薪酬不变

携程代表混合模式,爱彼迎代表纯自由模式。爱彼迎比较彻底,永久灵活,但注意是灵活,个人还是有权力回归办公室的。携程的混合模式更像是纯远程和传统的中间态,既提供了一定的自由度,也有约束条件定期回归公司以维系归属感。携程这个是更偏探索的一种状态,感觉是一个值得推广的好案例,他们的调研发现远程之后整体工作效率不降反升。

如果一种模式即没有损失工作效果,又给与了员工一定的自由,那就是应该推行,希望远程办公的趋势能来的更快一些吧。

面试解析

整理编辑:Hello World

CRC 实践应用:理论推导

CRC 全称叫做循环冗余校验(Cyclic Redundancy Check),是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数。常用于网络数据传输中的差错校验,其特征是信息字段和校验字段的长度可以任意选定。

类似作用的还有 奇偶偶校验、累加和校验等

理论基础一:多项式运算

CRC是基于多项式运算实现的,计算机系统中需要传输的数据可以看做是二进制表示的,例如数值42( 0x2A) = 0b 10 1010。二进制同时也可以改写成多项式格式表示(简化版):0b 10 1010 = 2^5 + 2^3 + 2^1(这里将系数为 0 的项删除,并省略了每一项的系数 1)。

CRC 算法推到过程,可以参考《循環冗餘校驗-维基百科》简介部分:

  1. 将数据转换成多项式格式,记作 M(x) 作为被除数。
  2. 和接收方协定一个多项式记作 K(x),作为除数 。专有名词被称为生成多项式
  3. 二者做除法。最终可以表示为 M(x) * x^n = Q(x) * K(x) - R(x)Q(x)看做是商,R(x)看着为余数。x^n 表示左移 n 位(等价于数据低位补 n 个零),n 的取值是生成多项式 K(x) 的位数 - 1。例如上面的 0b 10 1010 位数为 6,所以 n = 5。
  4. 然后将 M(x) + R(x) 的和作为新的数据传输到接收端,接收端收到后再除以多项式 K(x),如果余数为零说明数据没有修改,如果余数不为零说明 M(x) 部分有了变动,即数据发生了改变。

这里余数为零并不是绝对的表明数据没修改,存在数据改动后校验余数为零的可能,一般被称为误码率。也是衡量差错校验算法的指标之一。

实际编码中,K(x) 并不是随意选取的,而是使用一些已经规定的效率更高的多项式的值,根据 K(x) 位数不同,校验函数也命名不同,例如常见的 CRC-8、CRC-16、CRC-32、CRC-64等

例如 CRC-16 对应的生成多项式 PLOY 为:(x^16 + x^15 + x^2 + 1),表示为 0b 1 1000 0000 0000 0101 = 0x18005。

理论基础二:模 2 除法

上面讲了多项式格式转换以及作用。那么模 2 除法是做什么的呢?

在第三步中提到两个多项式做除法,实际这里并不是采取普通的除法运算,而是模 2 除法,特点是在进行余数计算时,针对每一位的加减运算皆采用不进位 & 不借位思想。即:

1
2
3
4
0 - 0 = 0;
0 - 1 = 1;
1 - 0 = 1;
1 - 1 = 0;

是不是看着很熟悉,对的,其实模 2 除法中的位加减运算本质上是二进制的位异或运算(xor)。

CRC 直接计算法

基于以上两个理论,我们总结下 CRC算法的步骤:

  1. 将要传输的数据用二进制形式展开,并选定一个生成多项式 K,K 的二进制长度为 W+1,则位宽为 W
  2. 在要传输的二进制数据后补 W 个 0,并初始化寄存器值为零。
  3. 用二进制展开后的数据用模 2 除法除以k,数据首位是 1 则商 1,为 0 则商零,最终计算得到的余数即为循环冗余校验和

根据上面的算法思想,我们这里通过一个计算实例验证:

上面的计算过程省略了商为 0 的余数计算,转换为程序逻辑代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define CRC_WIDTH   3
#define CRC_POLY 0x9 // 1001b

// 这里是需要进行校验的数据
int data = 0xB1C; // 0b 1011 0001 1100
// 左移补 0 这里 W = 3
data <<= CRC_WIDTH;
// 初始化结果
int crc = 0;
// 依次针对每一 bit 位进行计算 总位数 = 数据位宽 + 多项式位宽
for(int shift_bit = DATA_WIDTH + CRC_WIDTH; shift_bit > 0; shift_bit--) {
// regs << 1 目的是移除当前寄存器中最高位的值,使用剩余的bit 数据做计算,因为无论最高位是零或者 1,最终计算后都是 0,(最高位为 0 则商零 和 0 做异或, 为 1 则商 1 和多项式做异或,多项式最高位为 1 )
// (data >> (shift_bit - 1)) & 0x1是为了获取数据中指定位的值, & 0x1是为了只取最低位的 1 bit ,示例 1011 就是依次取 1、0、1、1
// 整句的目的是移除寄存器最高位数据,然后用待计算的数据补充
crc = (crc << 1) | ((data >> (shift_bit - 1)) & 0x1);
// 获取待计算数据的最高 bit 位是 0 还是 1, 如果商为 1 则需要 xor 异或计算,
if(crc >> CRC_WIDTH & 0x1) {
crc = crc ^ CRC_POLY;
}
}

CRC 直接计算法看上去就像在循环消除被除数的首位数据,每一轮计算都可以消除最高位 bit 值,得到余数后,继续计算消除首位。直到无法求商时,最终的余数值就是 CRC。

该思路虽然简单,但是如果需要被检验的数据很大时,每一个 bit 位都需要进行移位 & 异或操作,占用系统运算资源且相对速度慢。所以为了优化效率,采用空间换时间的思路。也就是驱动表法

进阶一 驱动表法(查表法)

驱动表思路是一次性计算好多位数据的 CRC 值存储在表中,使用时直接用对应位数的数据和表中的 CRC做异或。

由于计算机系统中一般以字节为最小数据单位,所以 CRC 表一般以 8 bit 作为最小生成单位,该思路利用了异或结合律(A Xor B Xor C = A Xor ( B Xor C ))。

示例表位宽简化为 4 bit 如下所示:

观察选中内容两种计算顺序,针对0b 1011每一位数据计算余数的结果和先进行除数的多次异或,然后再和数据 0b 1011异或的结果是一致的,驱动表就是这样实现的,提前计算初始化1011的余数值并存入表中,可以实现该值的重复利用,以空间换取后续同样数据的计算时间。

表的生成代码如下(以一字节为例),8 位二进制可以表示的数据范围为 0 ~ 255,针对每一个数据生成对应的 CRC 表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define CRC_POLY    0x11C
static uint64_t crc_table[256];

for (int i = 0; i < (1<<8); i++) {
uint64_t crc = i;
/* 每一字节数据都需要计算 8 次以求取 CRC 的异或值 */
for (int j=8; j > 0; --j) {
if (crc & 0x80) /* 判断最高位是否为1 决定商 1 或者商 0 */ {
/* 最高位为1,不需要异或,往左移一位,然后与 CRC_POLY异或 */
crc = (crc << 1) ^ CRC_POLY;
}
else {
/* 最高位为0时,不需要异或,整体数据往左移一位 */
crc = (crc << 1);
}
}
crc_table[i] = crc;
}

使用 CRC 表计算数据,当前数据最高位字节作为查表的索引,reg 专业名词叫做寄存器,可以理解为一个双字节的滑动窗口,初始值为 0,沿着数据字节链每次移动一个字节。

计算步骤如下:

  1. register左移一个字节,从原始数据中读入一个新的字节到寄存器的低字节位。
  2. 利用刚从 register 移出的高位字节作为 index 定位 table 中的一个 CRC 值,因为生成时也是以该数据计算的 CRC 值。
  3. 把这个 CRC 值 XOR 到 register 中组成下一次计算的新的数据,此时最高位的字节已经被消除。
  4. 如果还有未处理的数据则回到第一步继续执行。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned char   buff[] = {0x31, 0x32, 0x33, 0x34};
unsigned int len = sizeof(buff);
unsigned char *pointer;
unsigned int regs;

pointer = buff;
regs = 0;

// 在数据长度内迭代计算 CRC
while(len--){
// (regs>>8)&0xFF 是为了获取寄存器中的高位字节数据
// (regs<<8)|*pointer++ 操作时将寄存器高位字节移除,然后寄存器低位字节数据用我们需要计算的数据补充,相当于一次滑动操作。
regs = ((regs << 8) | *pointer++) ^ table[(regs >> 8) & 0xFF];
}

// 处理数据需要扩展的多项式位宽的补位数据,扩展的数据都是零, 根据 CRC_POLY 0x11C 扩展了 8 bit位一个字节
for(int i = 0; i< 1; i++) {
regs = (regs << 8) ^ table[(regs >> 8) & 0xFF];
}

以上是 CRC 的一个推导过程,下期周报将以 OOMDetecor 中的实例讲解实际应用中的变体操作有哪些。

优秀博客

本期优秀博客的主题为:App Extension。

1、iOS - App Extension 整体总结 – 来自博客园:俊华

@我是熊大:本文比较全面的介绍了 App Extension 的种类以及使用方法,平时不怎么使用的 Extension 竟然有十几种。通过此文应该能对 Extension 有个整体的了解。

2、App与Extensions间通信共享数据 – 来自博客:杨萧玉

@我是熊大:本文利用 WatchKit Extension 实现了 App 与 Watch 之间的通信,介绍了Containing app 与 App Extension 之间如何进行通信和数据共享。

3、Photo Editing Extension 详解 – 来自博客:colin

@我是熊大:本文通过一个 Demo 演示,介绍了 Photo Editing Extension 如何开发。

4、iOS14 Widget小组件开发(Widget Extension) – 来自简书:Singularity_Lee

@我是熊大:iOS14 之后出现了非常重要的 Extension,这就是 Widget,桌面小组件,本文十分详细的介绍了如何开发Widget,如果你也有开发需求,推荐阅读。

5、揭秘 iOS App Extension 开发 —— Today 篇 – 来自简书:Cyandev

@我是熊大:在iOS14之前,是没有桌面组件的,那时候叫做 Today Extension,但在 iOS14 之后,这个 Extension 已经被 Widget 代替。本文借助一个 todo 的 Demo,介绍了 Today Extension。

见闻

这一周阅读 / 浏览到的有趣的资讯。

1、Google I/O 2022: 促进知识和计算机技术发展 – 来自公众号:谷歌开发者

@远恒之义:备受期待的开发者科技盛宴 Google I/O 2022 于北京时间 5 月 12 日凌晨 01:00 正式开幕,Sundar Pichai 在大会开幕式上发表了主题演讲。硬件方面,主打性价比 Pixel 6a 售价 449 美元,Pixel 7 / 7 Pro 拥有三款新配色。Pixel Buds Pro 对标 AirPods Pro,是 Google 第一款支持主动降噪的 TWS 耳机。新的硬件 Pixel Watch 会与 Pixel 7 系列一道在 Google 秋季硬件发布会登场。搭载 Google Tensor 芯片的 Pixel 平板将画饼到 2023 年。软件方面,Android 开发领域迎来了大量新的特性与更新,Android 13、Flutter 3、Jetpack、Compose 1.2 beta、Large Screens、Machine Learning、Android Studio Dolphin (2021.3.1) 等等。

2、告别华尔街十年,我在非洲做起 WiFi 生意 – 来自公众号:志象网

@远恒之义:拥有传奇经历的周涛定居在非洲这片“最后十亿人”(last billion)的土地,目前正在肯尼亚创业,做“社区 WiFi”。他在国内做云计算的过程中结实了肯尼亚外交官 David,现在的合伙创始人。David 对肯尼亚目前绝大多数的用户不能廉价上网这件事情耿耿于怀,想要在肯尼亚发展互联网,必须先搞定昂贵的上网资费。周涛提供的公共 WiFi 网费非常低,做到了本土市场价格的 1/10。因地制宜,周涛大量雇佣当地社区青年来服务社区用户,打破了大家对非洲青年的固有印象。通过本地化的管理,公司的规模达到了 200 多人,其中大概只有五六个中国人。

3、Livid:十年V2EX,复古游戏,和未来 – 小宇宙:枫言枫语

@zhangferry:本期播客访谈的是 V2EX 的作者 Livid。聊了 V2EX 这十年的发展状况、老头环等复古游戏、还有如何用技术解决言论自由等问题。这里还学到一个新词「doomscrolling」,译为末日刷屏,指人们沉溺于刷负面新闻的状态。「互联网是很大的,你如果看到的互联网是这个样子,那是因为你去选择了关注这样的互联网」,关注正向的内容或者干脆不要受屏幕内容的影响,大家也可以好好考虑下。

说会 V 站本身,想想这么一个「简陋」的已经存在十年的 BBS 模样的网站,还依然有着很多忠实用户,其中程序员人群「浓度」一直很高,不免感觉神奇。不求变的东西为什么还能一直存在呢?后来我想了想,一个社区产品真正重要的东西不是花哨的装饰而是氛围,V 站有着其独特,小众的氛围,围绕这个氛围去吸引有相似属性的人。因为没有扩张阶段,这些领域的人会一直维持着较高同兴趣浓度,所以只要基本的功能,表达想法和交流想法的能力具备了,它就是一个完成态了。剩下的交给使用者去创造就行了,嗯,有意思。

4、Relingo – 浏览器插件

@zhangferry:一个帮助你学习英语的插件,使用起来是这样的。对于一篇英文文章,它可以自动根据设置等级翻译特定词汇,阅读本来就是非常高效的记忆单词技巧,加上这个自动翻译高级词汇的功能,真的非常便捷。还可以将自己不熟悉的单词记录到生词本中,有需要的快用起来吧。

5、图解iOS签名背后的原理 – 公众号:Bo2SS

@doubleLLL3:文章图文并茂地讲述了iOS签名背后的原理,讲述思路层层递进,容易理解,同时文章也参考了很多优秀的博客,并均在文末指出。

文章保证:阅读完全文会让你拥有轻松解决以下问题的能力:在真机上跑自己的iOS项目、iOS开源库以及公司的iOS项目。另外,文章的终极目标是让你在遇到任何 iOS 签名相关问题时,都能够让问题快速解决。

学习资料

整理编辑:zhangferry

Skr_Learning

地址https://github.com/Kiprey/Skr_Learning

这里将定期记录着一些与Sakura师傅以及一群小伙伴共同学习的内容与进度。是一篇学习汇报,内容包括但不限于C++ STL、编译原理、LLVM IR Pass代码优化、CSAPP Lab、uCore操作系统等等。持续更新ing…

感觉 Github 也是一种很好的记录学习过程的平台!

工具推荐

整理编辑:CoderStar

IINA

地址https://iina.io/

软件状态:免费

软件介绍

适用于 macOS现代 媒体播放器,IINA 由开源媒体播放器 mpv 提供支持,几乎可以播放您拥有的所有媒体文件。

iina

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS 成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS 摸鱼周报 #52 | 如何规划个人发展

iOS 摸鱼周报 #51 | 游戏版号恢复发放

iOS 摸鱼周报 第五十期

iOS 摸鱼周报 第四十九期

macOS 进化史

macOS 进化史

macOS 最为 iOS 开发的钦定操作系统,且 iOS 本身就是通过它衍生出来的,所以我们跟它之间会经常打交道。为了验证你对它的熟悉程度,看下能否回答这几个问题:

  • 我们在说 MacOS 时通常会带上「X」,将其叫做 MacOS X 或者 OS X,这是为什么呢?「X」 有什么含义?
  • Darwin、XNU、Mach、BSD 分别代表什么,之间又有什么关系?
  • Mac OS 与 Unix 是什么关系?
  • Apple 开源了 Darwin,对应的开源社区为什么发展不起来?

本篇文章致力于帮助解答这些问题,也会顺道讲些 Apple 相关的背景小故事。

文章内容主要参考《Mac OS X And iOS Internals》(中译本叫《深入解析Mac OS & iOS》)和 《*OS Internal Volume I – User Mode》,作者都是 Jonathan Levin。前者完书于 2012年,后者第二版本完书于 2019 年,后者不仅是前者的完善版本,还是一个全新版本,大量图文都进行了重写。随着时间的推进,后者对最新的技术有了更多讨论。

MacOS 发展背景

背景

MacOS 的早期版本叫做 Mac OS Classic,它诞生于苹果,拥有伟大的 GUI 却是一个相对粗糙且很不成熟的操作系统。

在这期间苹果创始人乔布斯离开苹果创办了 NeXT,NeXT 公司生产 NeXT 计算机和NeXTstation,它们运行在叫做 NeXTSTEP 的操作系统之上。NeXTSTEP 有这些比较前卫的特性:

  • 采用 Mach 微内核
  • 使用 Objective-C 作为开发语言
  • 面向对象思想贯穿整个操作系统
  • 设备驱动开发是一个面向对象的框架,叫做 DriverKit

MacOS X 诞生

后来乔布斯回归苹果,也将 NeXTSTEP 带回了苹果。于是自然而然的,Mac OS Classic 和 NeXTSTEP 两个非常小众的操作系统进行了融合。他们一个拥有伟大的 GUI 但设计糟糕,一个设计很棒但 GUI 平淡,融合之后起到了 1+1 大于 2 的效果,诞生了一个流行的多的操作系统,这就是 MacOS X。

MacOS X 此时的几个核心组件:Cocoa、Mach、IOKit、Xcode 的 Interface Builder 都来自于 NeXTSTEP。这个操作系统的内核就是 Darwin(中译为达尔文)。

Darwin 是开源的,以它为核心诞生了 iOS、tvOS、watchOS、BridgeOS(用于 Macbook Touch Bar 的 OS) 等一系列变体操作系统。有一个命令可以查看系统所使用的 Darwin 版本信息:uname。

1
2
$ uname -v
Darwin Kernel Version 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64

Darwin 和 Darwin 变体的一系列 OS 版本是同步更新的。它们之间的版本遵循这个关系:

Darwinver = 10.(MacOSVer + 4) = (iOSVer + 6) = (TvOSVer + 6) = (WatchOSVer + 13)

后来 MacOS 的主版本号从 10 升级为 11,上面 MacOS 的版本对应关系发生了一些变化。到了这里有必要再理一遍 MacOS 的名称变化情况,确实很少有人能准确的称呼它,因为它的命名发生过不少变化

时间段 MacOS 名称 说明
创建 ~ 2001 年 MachOS Classic 古典 MacOS
2001年 ~ 2011 年 Mach OS X NeXTSTEP 与 MacOS Classic 合并之后的版本
2012 年 ~ 2015 年 OS X 这是最后一个以猫科动物命名的 OS 版本,此后开始以加州地标命名
2016 年至今 macOS 便于与iOS、tvOS、watchOS 命名统一

为了便于混乱,从这开始的下文在讲到 MacOS 统称时均以 macOS 代替。

Darwin 操作系统的演变历史。

图片来自(*OS Volume 1)

结合上面图片可以再讲一个 iOS 的小故事,iOS1.x 版本最初的代号是 Alpine,这是 i 系列设备的默认 root 密码。但最后发布的版本代号是 Heavenly,因为这个版本的操作系统拥有完整的调试符号、未加密还容易反汇编,很多越狱者都依赖从这个版本中提取的符号和函数调用关系寻找破解灵感,从越狱者角度来看确实如天堂般美好。

Darwin 的内部组成

Darwin 是一个类 UNIX 的操作系统核心,它的组成可以近似看做:Darwin = kernel + XNU + 运行时。macOS 从Leopard(10.5) 开始已经是一个经过认证的 UNIX 实现。

XNU 是一个占据关键作用的 Darwin 核心,XNU = Mach + BSD + libkern + I/OKit。

初版的 XNU 是 NeXTSTEP 的核心,它包括 Mach 2.5 版本和 4.3 版本的 BSD。NeXTSTEP 合入苹果后,Mach被升级为 3.0,BSD 升级为 FreeBSD。

Mach 和 BSD 一个是微内核(Microkernel)一个是宏内核(Monolithic Kernel),所以 XNU 是一个混合架构(Hybrid kernel)。理解这几种内核的关键是需要注意内核模式和用户模式占据的范围。

Mach

Mach(微内核)由卡耐基梅隆大学开发,它的目标是取代 BSD 的 UNIX 核心。这个微内核仅能处理最基本的操作系统职责:

  • 进程和线程抽象
  • 虚拟内存管理
  • 任务调度
  • 进程间通信和消息传递机制

由上图可以看出微内核的功能本来就少,其他 OS 功能是作为基础服务建设在用户模式下的。因为这个特性其内部任务的调用会有更频繁的内核态/用户态上下文切换,这会额外消耗时间。同时内核与服务进程之间的消息传递也会降低运行效率,所以这种设计通常会降低性能。

但它也有优点,就是服务进程容易扩展,服务进程出问题不会危及到 kernel 。得益于这种扩展性 MachO 能支持多架构文件,以此为基础 macOS 能顺利的从 PowerPC 过渡到 Intel 再到 M1。

BSD

BSD(宏内核),它是 Berkeley Software Distribution (伯克利软件包)的缩写,这是一个派生自 Unix 的操作系统。BSD 是作为完善 Mach 的一个存在,它建立在 Mach 之上,并提供了一层更可靠更现代的 API。它主要包括这些:

  • UNIX 进程模型
  • POSIX 线程模型
  • UNIX 用户和组
  • 网络协议栈(BSD Socket API)

宏内核的特点是用户服务和内核服务都运行在同一内存空间,这还有效降低了内核态/用户态之间的频繁切换,执行效率会更高。但是宏内核也并非没有缺点,就是扩展性较差,另外如果内核有一个服务崩溃,整个操作系统就会崩溃。

Darwin 架构

既然没有完美的内核模式,于是苹果就将两者混合,它同时兼顾微内核和宏内核各自的优点,这就是 Darwin了。

图片来自(Mac OS X And iOS Internals)

这里没有表示出 XNU,它的边界可以看做是 Kernel/User Transition 这里,其下包括 BSD 和 Mach 的层级就是 XNU。在 macOS 的体系里,Darwin 之上的层次基本都是不开源的,他们是 Apple 的私有财产。

XNU 中还有另外两种重要组件:

  • libkern:这是一个内建的 C++ 库,用于支持 C++ 运行时。有了它内核的很多高级功能都可以使用 C++ 编写。
  • I/OKit:这是一个设备驱动框架,凭借 libkern 提供的底层支持,驱动程序可以使用 C++ 实现。借助于 C++ 的面向对象特性,外部在创建驱动程序时会节省很多成本。

Darwin 的开源之路

既然 Darwin 开源了,那为什么没有出现非 Apple 系的 Darwin 发行版操作系统呢?虽说开源,但 XNU 主要依赖的 Mach 和 BSD 本来就是开源的;Apple 还把对 ARMv7/8 的支持单独闭源;原本来源的 launchd,在 Mac OS 10.10 的版本之后也变成闭源项目合入到 libxpc 项目里了。这还不算,Darwin 的开源版本并没有剥离干净,里面还包含了一些 Apple 的私有 API,导致其并不能完整编译,还需要做一些额外改造。

围绕 Darwin 有两个重要的开源版本,OpenDarwin 和 PureDarwin ,可以看下他们当前的发展状况。

OpenDarwin

它由 Apple 牵头于 2002 年 4 月成立,其目标是加强苹果开发人员与自由软件社区之间的协作且将 Darwin 发展出另一独立版本。理想情况是苹果可以将 OpenDarwin 中的改进应用到 Darwin 中,而开源设计又可以完全控制该系统,将其用于 GNU-Darwin 等自由软件的发行版中。然而仅仅过了 4 年,OpenDarwin 就宣布关闭。以下是 OpenDarwin 项目组的陈述

Over the past few years, OpenDarwin has become a mere hosting facility for Mac OS X related projects. The original notions of developing the Mac OS X and Darwin sources has not panned out. Availability of sources, interaction with Apple representatives, difficulty building and tracking sources, and a lack of interest from the community have all contributed to this. Administering a system to host other people’s projects is not what the remaining OpenDarwin contributors had signed up for and have been doing this thankless task far longer than they expected. It is time for OpenDarwin to go dark.

主要因素有两个:

  • 苹果的 macOS X 对 OpenDarwin 掌控过强,未推进 OpenDarwin 的独立发展
  • 开源社区的兴趣减淡。也可以说是前者导致了后者

现在 OpenDarwin 的官网 http://opendarwin.org/ 仅剩一行字:Opendarwin memorial page。

PureDarwin

PureDarwin 通常被认为是 OpenDarwin 的继承者。它的代码托管在 Github上,且仍在维护。Pure 的含义是更纯净,PureDarwin 仅使用苹果为 Darwin 发布的组件而不用 macOS 的其他组件。它的目标是通过提供文档,使开放源码爱好者和开发人员能够检索、理解、修改、构建和分发 Darwin,从而使Darwin 更易于使用。

因为缺少官方的支持,它当前在 Github 上的 star 数仅有 1.7k,由此可见它的关注度和发展都不算太好。但是当有人问为什么要花费时间维护 PureDarwin 时,他们的答案是:

For learning and for fun.

简短却让人振奋,与此同时还有那么一丝丝凄凉。

所以回归上面的问题为什么开源的 Darwin 没有发展起来,因为它是为 macOS 创建的操作系统,它依赖于 macOS 的特性,也依赖于 Apple 的支持,脱离这两者尝试走「 Pure」Darwin 开源路线是非常困难的。

Hackintosh

OpenDarwin 和 PureDarwin 的发展仍带来了一些有益的事情,其基于开源的 Darwin 制作成一个可以完整引导并且安装的 ISO 镜像。之后 OSX86(一个致力于把苹果电脑出品的 macOS 操作系统移植到非苹果电脑上的计划)项目在此基础上继续发扬光大,努力将 macOS 完整移植到 PC、笔记本等设备,该行为被称为 Hackintosh,也叫黑苹果。

通常的黑苹果方案是借助于引导程序实现的,因为它不会修改 macOS 源文件,被称为最佳的合法途径。苹果曾经开源过 Boot-132,一个用于加载 XNU 内核的引导程序。Voodoo 团队基于该程序开发出 Chameleon(变色龙)引导程序,再后来 Clover 出现,可以让不支持 EFI 的电脑进入模拟的 EFI 环境。现在又有了 OpenCore,它在配置文件时比较复杂,但因其受到较多 kexts 作者的兼容和本身的易用性而得到相当数目使用者的追捧。

关于合规的问题,虽然引导的方式没有修改 macOS 的源码,但苹果的最终用户许可证协议(EULA)里并不允许将 macOS 安装在一台没有苹果商标的硬体上。苹果曾起诉并多起黑苹果相关的商业行为并获得胜诉,对于非盈利的个人 Hackintosh 行为,苹果并没有过多理睬。

吉祥物的故事

人们热衷于为受欢迎的操作系统或者框架设置吉祥物,Linux 的吉祥物是一只企鹅(名叫 Tux,Torvalds UniX的缩写)、安卓的吉祥物是一只绿色小机器人(无正式名称,被开发人员称为 Bugdroid)。再看下跟 macOS 相关的两个操作系统的吉祥物。

BSD

BSD 的吉祥物是一只小恶魔😈,叫做 Beastie,它的发音跟 BSD 很像。它通常带支三叉戟,代表行程的分岔。

Darwin

Darwin 的吉祥物是 Hexley,它是一个卡通的鸭嘴兽,戴着 BSD 小恶魔的帽子,也拿着三叉戟。Hexley 是由 Jon Hooper 所设计的,版权也为他所有。但 Hexley 并不附属于 苹果电脑。本来这个吉祥物的名称应该是 Huxley,源由是捍卫达尔文(Darwin)进化理论的英国生物学家 Thomas Henry Huxley,而原先提议的人误以为是达尔文的助理,并错用了 Hexley。而发现错误时,要改名已经太晚,因此沿用了 Hexley 这个名称。

这个形象并不属于 Apple,而属于开源社区,所以开源版本的 Darwin 均有该图案的展示。

「*OS Internal 三部曲」的书籍封面就是用的 Hexley 形象。

未来展望

对于 macOS 未来的展望这部分内容摘自《Mac OS X And iOS Internals》(注意其完成时间是 2012 年),站在 10 年后的今天我们可以再去看下这几个预测的实现情况。

根除 Mach

内核中的 Mach API 是 NeXTSTEP 时代的产物,运行速度慢,执行效率上很难赶得上 BSD。而且 XNU 本身更趋向于宏内核架构,如果移除 Mach 将内核建设为完整的 BSD,将会有很大收益,但这确实需要巨大的工作量。

该想法并没有达成,且 Apple 根本没有这么做的打算,混合内核并没有看上去那么遭,Mach 将在很长一段时间继续存在着。

兼容 ELF 格式

macOS 无法融入 UN*X 的世界最大的一个困难就是坚持使用 Mach-O 二进制格式。当然这个想法也需要依赖上一步的根除 Mach,这样 Linux、BSD 中的程序就可以不经修改直接迁移到 macOS 上了。

这是很美好的想象,考虑 Apple 对 Hackintosh 的打压,其商业策略是独占、完全掌控而非扩大市场占有率。

使用 ZFS

macOS 早期使用的文件系统是 HFS+,但该文件系统遭受过大量批评 Linus 曾这样评价 HFS+:

Quite frankly, HFS+ is probably the worst filesystem ever. Christ what shit it is.

HFS+ 确实有很多不完善的地方,它大小写不敏感、不支持对数据内容进行 checksum 校验、timestamp 只支持到秒级。彼时 Sun 公司开发出 ZFS,号称是「宇宙无敌最强」文件系统,有传闻 Apple 将使用这一文件系统,但后来 Sun 被 Oracle 收购,这一想法最终无法实现。

2017 年, 伴随着 Mac OS High Sierra 版本,Apple 正式发布了 Apple File System(APFS)。该文件系统是 Apple 从零开发的,耗时三年,对于完整的文件系统来说,这个效率已经非常高了。其支持更多的功能,且宣称针对 SSD 做了很多优化。但让人遗憾的是 APFS 在多个方面的性能还没有超过 HFS+

和 iOS 合并

在 macOS 和 iOS 的发展过程中,有不少功能都是在一个平台成熟之后移植到另一个平台上的。即使在 M1 芯片出现之前,这一想法也是有可能的,Apple 在很早之前就实现过硬件架构翻译机制 – Rosetta。

当 M1 芯片发布之后,macOS 和 iOS 都已经可以运行在 arm64 架构的芯片上了,这个想法似乎顺其自然要实现了。但现实并非如此,相对于统一,Apple 更想要的是各司其职,每个产品线 macOS、iPadOS、iOS 都有其各自适用的场景,Apple 也极力往这方面宣传,这有助于产品的销售。

总结

再次列出开头提到的几个问题便于大家回顾这篇文章的内容:

1、我们在说 MacOS 时通常会带上「X」,将其叫做 MacOS X 或者 OS X,这是为什么呢?「X」 有什么含义?

2、Darwin、XNU、Mach、BSD 分别代表什么,之间又有什么关系?

3、Mac OS 与 Unix 是什么关系?

4、Apple 开源了 Darwin,对应的开源社区为什么发展不起来?

iOS 摸鱼周报 52 | 如何规划个人发展

iOS 摸鱼周报 52 | 如何规划个人发展

本期概要

  • 话题:互联网行业不景气,个人该如何发展
  • 面试模块:load方法为什么耗时?
  • 优秀博客:扩展视野,了解其他领域的动态
  • 学习资料:Python 最佳实践指南
  • 开发工具:Bartender是一款很棒的菜单栏管理工具,有效解决当屏幕比较小时,顶部菜单栏显示不全的问题;

本期话题

互联网行业不景气,个人该如何发展

@zhangferry:本期话题来源于上周参加的亚东组织的一次分享会,分享者是做猎头的Firda,正常分享持续了3个小时且质量非常高,我简单总结下这次听讲的收获。

互联网行业多个大厂从股票暴跌到不断裁员,行业不景气为何有的人被迫转行?有的人却还能激流勇进?不同人的发展轨迹不同,其中很重要的一点就是他们做的选择不同,我们应该做的是:做对的选择,正确努力

前半句是做选择,要做对的选择就需要拥有这些选择的足够信息,了解越多,越不容易犯错。我们需要做到以下几点,外部:

  • 能分析外部环境市场&机会
  • 能判断当下自己在公司内所处局势
  • 能判断在什么时候可以看看机会

内部:

  • 自知,较为清晰自己在市场中的能力水平

这里着重提一下内部因素这一点,找到市场中同工龄的人应具备哪些能力,应达到什么样的标准。这个可以通过跟领导的交流、招聘网站或者一些大牛的个人成长经历分享中获取。注意这些应该是相对优秀的人,然后以他们为标杆进行学习。我现在回想自己成长比较快的几个阶段,都是因为遇到了比自己优秀很多的同事,然后学习他们好的习惯。

另外一点是正确的努力,只努力不行,方向要正确。职场不同于学校,它要求的不是单一能力,而是综合能力。如何学习新知识、如何更好的协作、如何复盘、如果应对复杂的业务需求、如何构建自己的高效工作流、如何带团队、如何向上管理等等,在努力自我更新的同时也要注意这个更大的环境所发生的变化,结合自己的需求去往不同方向延展。以上每个点都有很大往里探索的空间,职场成长也应是个人成长,在努力做好工作的同时我们也在努力塑造一个更强大的自己。

回过头来,互联网不景气了,个人该如何发展呢?大环境基本无法控制,只能是从自身出发,自己如果做到无可替代,那就无惧外接环境变化。找准定位,正确的努力。

面试解析

整理编辑:JY

load方法为什么耗时?

我们都知道启动优化的时候,减少+load方法能够减少启动时间。

如果+load 方法里的内容很简单,会影响启动时间么?比如这样的一个+load方法?

1
2
3
4
+ (void)load 
{
printf("123");
}

这段代码编译完之后,这个函数会储存在Mach-O中的TEXT两个段中,__text存函数二进制,cstring存储字符串 123

要执行printf函数,首先需要访问__text触发一次page In 读入物理内存,为了要打印字符串,还需要访问cstring,还会触发一次page In

有很多同学不了解page In,这里介绍一下,首先先要知道mmap

mmap 的全称是 memory map,是一种内存映射技术,可以把文件映射到虚拟内存的地址空间里,这样就可以像直接操作内存那样来读写文件。

当读取虚拟内存,其对应的文件内容在物理内存中不存在的时候,会触发一个事件:Page In,把对应的文件内容读入物理内存中。

Page In又做了哪些事情呢?

  • MMU找到空闲的物理内存页面

  • 触发磁盘IO,把数据读入到物理内存

  • 如果是TEXT段的页,要进行解密(iOS13之后不需要解密)

  • 对解密后的页,进行签名验证

    为了执行这个函数,系统付出了两个page In的代价,所以一旦load方法过多,会影响启动速度

优秀博客

本期将介绍一些有特点的中文技术博客。虽然其中大多的内容与 iOS 或 Swift 关系不大,但对于开扩视野、了解其他领域的动态很有帮助。

整理编辑:东坡肘子

1、漩涡的博客 – 来自:Xuanwo

@东坡肘子:作者是一名受雇于开源项目 datafuselabs 的全职开发者,工作中主要使用的是 Rust 语言。每周他都会介绍当周的项目进展,包括:技术分享、开源运营、开发感受等。从中可以对商业运营的开源项目的参与和运作有所了解。

2、科技爱好者周刊 – 来自:阮一峰

@东坡肘子:内容包罗万象,包含技术分享、软件推荐、科技动态、奇闻异事等等。最大的特点是读者的参与度高,留言踊跃。

3、codedump的网络日志 – 来自:codedump

@东坡肘子:作者对存储引擎、分布式开发、缓存服务等内容情有独钟,撰写了大量的相关文章。近期开始从 C 转向 Rust ,估计今后有关 Rust 的内容也会逐渐增多。

4、体验碎周报 – 来自:龙爪槐守望者

@东坡肘子:作者是一个交互设计师,每周的博文都会汇总大量有关交互设计的文章、案例、动态、评论、分析等内容。即使你从事的工作与交互无关,从中也能有所收获。

5、13 的 Apple 开发者周报 – 来自:Ethan Huang

@东坡肘子:以 Twitter 上的信息汇总为主,内容包括:苹果官方消息、媒体报道、技术资源、趣事小梗等内容,并且每周还会制作一个由苹果开发者参与的 podcast。对了解对岸开发者的开发状态有一定的帮助。

见闻

这一周阅读/浏览到的有趣的资讯。

1、你真的了解二维码吗? – 来自公众号:中兴文档
@远恒之义:如今二维码和我们的生活息息相关,扫码加好友扩大社交圈,线下扫微信和支付宝购物,出行查看行程码与健康码,这些都是最常见的使用场景。那么,有如此广泛用途的二维码你真的了解吗?文中详细的介绍了二维码的组成区域和工作原理,回答了二维码是否会被耗尽以及怎样才能安全地扫描二维码等相关问题。

2、AI 绘画能代替真人么?我们和几位设计大咖聊了聊 – 来自公众号:设计青年实验室
@远恒之义:如果你关注艺术和设计,那么最近也许会被一款外网爆火的 Disco Diffusion AI 刷屏。Disco Diffusion 是一款可以根据描述场景的关键词渲染出对应图像的 AI 图像生成程序(你说我画)。面对功能如此强大的 AI 绘画神器,作者非常好奇关于它背后的一些故事:Disco Diffusion 的创作的逻辑是什么?生成的画作版权归属怎么算?以及大家讨论度最高的话题,Disco Diffusion 的出现是否意味着人工绘画会被电脑代替?

3、WebAssembly完全入门——了解wasm的前世今身 – 来自知乎:SH的全栈笔记

@zhangferry:我开始对 WebAssembly 一直不理解,直到阅读到这篇文章才恍然大悟。 看官网的定义

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

我们再把WebAssembly 拆开看:Web + Assembly,Web 格式的汇编!对,这就是 WebAssembly 的精髓。它并不是高级的编程语言,而是一种类汇编的、可移植的、兼容 Web 的二进制格式(format。有时也把它成为语言,我感觉按格式来理解更清晰一些)。更进一步来说 WebAssembly 就类似是 Java 中的 Bytecode,iOS 中的 BitCode,它是一种中间码。

那为什么会出现这个东西了?它是用于解决什么问题的?

这个是 Mircsoft Edge 开源的 ChakraCore 的执行流程,可以看出对于 JavaScripts 这种解释性语言的执行,调用过程过程要进行解析和生成 Bytecode 的操作是比较耗时的。那能不能想编译型语言一样,直接给它一个“编译文件”,将编译任务提前做好,给到解释器呢?可以,这个工作就是 WebAssembly 诞生的目的。它不光是能节省 JavaScripts 的执行时间,还凭借其可移植性,可以将一些用 C/C++/Rust 等创建应用直接移植到Web端,而无需再搞一遍 JavaScripts实现。现在 AutoCAD、GoogleEarth、Unity 等一些较大型的项目都借助于 WebAssembly 移植到了 Web 端。

4、Doodle Icons

@zhangferry:一款涂鸦风的图标网站,有 400+ 可用 ICON,除了ICON 还有一些涂鸦插画,个用和商用都免费。相对于标准规范的流水线图标,涂鸦风格能给人带来一种更加轻松欢快的感觉,在一些特定场合我们可以将图标换成这类风格试一下。

5、星链 Starlink

Space X 的星链用一种更直观的方式可以更好的感受它的存在,上图是官网给出的星链卫星分布图。可以看到星链已经覆盖了很多地方,图片左下角的数字区域表示,在 4 月 28 这一天,一共有 2336 颗卫星,正在使用的是 1730颗,损毁的有 211,其余是未激活状态。2300 多颗是什么概念,这已经达到我国历史发射卫星总量的 3 倍。

星链的目的是提供全球卫星互联网系统,19 年马斯克借助星链成功发送了 twitter,目前星链提供的能力已经覆盖仅30 个国家。

学习资料

整理编辑:Mimosa

Python 最佳实践指南

地址https://pythonguidecn.readthedocs.io/zh/latest/

这是一份关于 Python 的实践指南,该指南目前持续不断地更新与完善,在 Github 上有 5.8k 的 Stars。它旨在为 Python 初学者和专家提供一个关于 Python 安装、配置和日常使用的最佳实践手册。涵盖各种平台的 Python 安装、优秀的模块推荐、配合不同的 web 框架和工具、如何写出优雅的 Python 代码等内容。链接中的这份是该指南的中文版。

工具推荐

整理编辑:CoderStar

Bartender

地址https://www.macbartender.com/

软件状态:免费

软件介绍:可以免费试用4周,购买费用 $15;

Bartender是一款很棒的菜单栏管理工具,有效解决当屏幕比较小时,顶部菜单栏显示不全的问题;

Bartender

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS 摸鱼周报 #51 | 游戏版号恢复发放

iOS 摸鱼周报 第五十期

iOS 摸鱼周报 第四十九期

iOS摸鱼周报 第四十八期

人物访谈 | 人在上海的东阁堂主

人物访谈 | 人在上海的东阁堂主

东阁堂主是我非常早就关注到的一位开发者,他写过不少开源库,个人 Github 地址是:https://github.com/dudongge 。本人非常热爱技术,最近翻译了一本 iOS 架构相关的技术书。因为他在上海,还处于封控期间,除了聊工作也会聊一下他目前的生活状态。

简单介绍下自己吧

大家好,我是东阁堂主,在成为码农之前,在车企里做过售后,仪器厂做过测试,电子厂做过机修。受室友影响,15 年入坑 iOS,目前在 B 站的漫画事业部。

工作相关

能简单分享下你的工作内容吗?

目前我在项目负责哔哩哔哩漫画国内版和国际版的需求开发,也会做些性能优化,比如启动优化,包体瘦身,核心页面的秒开优化等。从 20 年开始,B 漫就在项目中嵌入 Flutter,目前新需求基本都是 Flutter 开发。Flutter 开发页面很快,开发人员没有增加的情况下,开发效率反而大幅提升了。当然测试效率也会提高,之前要两端都要测试,现在只有着重测试一端,另一端测测兼容即可。

最近在上海的工作状态怎么样,生活各方面造成的影响大不大?可以分享一些这段期间学到的「封城」生存之道。

受疫情影响,我不得不蜗居在公司附近的出租房里,工作可以在家办公,但效率会稍微打折(主要是没有大屏的显示器)。狭小的空间限制我的躯体,却限制不了我的灵魂,我一向比较乐观,最近也是被抢菜搞得有些疲惫,生活物资基本全靠团长和政府救济,不能想吃什么吃什么了。总结一些封控期间囤物资的技巧吧:

  • 如果有官方消息要留在家中,可乘空闲时候去附近超市采购,如果厨具齐全,米面油,杂粮,速食,干蔬之类的可多备点,注意,在采购东西时一定要注意安全。

  • 封控期间可在 App 上抢东西,比如叮咚,美团,盒马,他们会在规定的时间内开放购买入口,要先把东西提前加入购物车,因为等开放时刻再加,基本上是来不及的。用安卓手机的可以在网上下载个抢菜插件(https://github.com/Skykai521/DingDongHelper),iOS 用户有筋膜枪就用筋膜枪吧。还有就是注意捡漏,时不时刷一刷,厂商也会时不时补货。等货到后,要注意货物表面消毒。

  • 加入小区群,这个是最靠谱,最省时省力的方式。团长发起团购消息时,及时回应,因为团购有截止时间。我加入的团购群有猪肉群,鸡蛋群,蔬菜群,牛奶群,面包群等。有什么需求,可以直接在小区群里咨询,有富裕的东西可以物物交换。

  • 找跑腿小哥,不过会加相应的跑腿费,只要价钱合适,会有小哥接单的,让小哥代买一些食品。

在 Bilibili 工作是一种什么体验?

在 B 站漫画部门上班,氛围比较轻松,也算是弹性上班,公司鼓励奇装异服,彰显个性,可以带宠物上班,也有很多流浪小动物寄养在公司,哦对了,入职即送 B 站大会员🍻。

iOS 开发没人要了,网上有很多这样的劝退论调,结合你跟 iOS 之间结下的渊源与实际的工作情况来说一下你对这个现象的看法。

我是 15 年踏入 iOS 开发行列的,那时移动端正值繁花卓锦,烈火烹油的时代,各行业都想分一杯羹。所以 iOS 开始开发者岗位也是与日俱增,19 年的时候大浪淘沙,行业洗牌,很多公司倒下了,也直接导致了 iOS 的岗位减少。现在初级的 iOS 需求量确实少了,但还是有的,就像 B 站每年都有 iOS 实习生的名额。至少到现在,我认识的 iOS 开发者都有工作,当然打铁还需自身硬,提高自己的技能才是王道。

学习相关

看到你整理翻译的这本 iOS Architecture 书,能简单介绍它的主要内容吗?做这件事的出发点是什么?一共花费了多长时间?最终的收获又是什么?

这本书前四章会介绍架构的理论基础,会涉及到一些常用的设计模式,结合例子给出具体的代码实现,和读者一起讨论哪种架构适合自己的业务,以及使用架构会带来哪些益处。接下来几章会着重介绍MVVM、Redux、Element 架构的具体实现,使用时注意事项以及优缺点。最重要的是可以启发我们思考,当前我们的项目有哪些可以改进的地方。

因为之前没有系统想过 iOS 架构相关东西,想在这方面深入了解一下,就找到了这本书。整理和翻译花费近两个月,通过这本书的完成,使自己对通用架构有了更深一层的认识,感觉自己的 English 没有白学😅,当然体会到了翻译的不容易,算是完成了自己今年初定下的第一个目标吧。

对于一些非工作项的事情像是翻译书籍、写开源库,你是如何自我驱动来实现的。会不会遇到一些阻力,遇到阻力的话是如何克服的?做这些事情有没有给你带来一些意外收获?

翻译整理书籍主要就是想挑战一下自己,也希望可以帮助其他人,自己也是看别人翻译的文章和分享的库来满足日常的开发需要。写开源库,也是记录自己学习的一种方式,或许能帮到有类似需求的小伙伴。遇到的阻力就是时间节点问题吧,开始以为很快就能完成的,事非经过不知难,制定的计划会被其他干扰因素打破(比如上海这次疫情),根据实际情况修正计划,当胜利的曙光到来时,自己的幸福满足感爆棚。

结合自己的经历,能否分享一下对你来说好的学习经验和学习习惯?

工作中的经验积累很重要,要时不时的总结一下,好记性不如烂笔头,这些经验或者教训可是经过实际检验的,比自己写的 Demo 更具有可操作性。平时就是多看技术博客和技术公众号,多和组内的人交流。

可以在油管上找些教程,YouTube 可以自动生成字幕,不用担心听不懂。

这里特别强烈推荐一款应用,就是苹果自己家的:Developer。这款应用有 WWDC 相关的技术视频,可以缓存下来,也有字幕,闲时找自己感兴趣的看一看,还是会有收获的,稍微介绍一下,说不定你就会爱上它。

个人爱好

除了作为开发者的身份,还有其他什么兴趣爱好可以分享的吗?

平常爱运动,也爱旅游,参加过几次马拉松,喜欢中国古典诗词文化,偶尔也会写首顺口溜。

之前在群里有看到你发的桌面照片,有很多手办,这是 B 站的风格还是你的风格。补几张照片让我们都开开眼吧

我不是真正的二次元,但也不排斥二次元文化,我的手办不多,补几张大佬的桌面吧。

作为 B 站人,肯定少不了刷 B 站,推荐几个你感觉不错的up主吧。

硬核的半佛仙人:有搞笑的配图和内容,给平淡的生活增加一些笑意。

罗翔说刑法:让你在故事中读懂法律。

冒险雷探长:算比较早是旅游达人,可以领略到到国外的风景。

再推荐几个入站必看视频吧:

再推荐一本书或者一个开发工具吧

在这里我不推荐学习相关的东西了,推荐一本书吧《明朝那些事》算是一套白话历史的丛书书,讲的诙谐生动,用现代人的观点解读当时的情境。网上也有相关音频,地铁上,睡觉前,都可以听一听,作为消遣娱乐。

可以谈一个自己最近才明白的心得、感受或者体会吗。

生于忧患死于安乐,家里常备粮,心里才不慌,意外和明天真不知道哪一个会提前到来,善待自己,善待他人。

有没有想借助于摸鱼周报宣传的。

漫画部门暂时没有 iOS 坑位 (⊙︿⊙),其他部门有,有看上的可以直接投递,也可以敲我:

iOS摸鱼周报 51 | 游戏版号恢复发放

iOS摸鱼周报 51 | 游戏版号恢复发放

本期概要

  • 话题:游戏版号恢复发放,45 款游戏获版号
  • 面试模块:简述 mmap 应用
  • 优秀博客:iOS 内购主题
  • 学习资料:一本刚翻译的 《iOS 高性能 App 架构》小书
  • 开发工具:Quiver是为程序员打造的笔记本,可以轻松混合文本、代码、MarkdownLaTeX。iTab 一款可以自定义 Tab 页的浏览器插件。

本期话题

游戏版号时隔8个月恢复发放,4月45款游戏获版号!

@iHTCboy:4 月 11 号下午,很多游戏公司的老板在朋友圈透露,今晚发版号了!从 2021 年 7 月 22 日至今,8 个月后游戏版号迎来重启。对我们游戏行业者来说无疑是一个好消息,因为有版号就有新游戏可以上线,对于游戏市场也是一个好的信号,一个全新的开始。另外,4 月 15 号,国家两部门联合通知:严禁网络视听平台传播违规游戏。未经主管部门批准的网络游戏,不得通过各类平台进行传播。

网络直播乱象、青少年沉迷游戏等问题,也一直是全民关心的问题,所以,游戏作为一个重要的内容创作平台,应该传递更多正能量,理性表达、合理消费,共同维护文明健康的网络视听生态环境。

面试解析

整理编辑:Hello World

mmap 应用

mmap是系统提供的一种虚拟内存映射文件的方法。可以将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程中虚拟内存地址的一个映射关系。

在 iOS 中经常用在对性能要求较高的场景使用。例如常见的 APM 的日志写入,大文件读写操作等。

mmap还有可以用来做共享内存进程通信、匿名内存映射,感兴趣的同学可以自行学习

普通I/O流程

普通的读写操作,由于考虑虚拟内存权限安全的问题,所有操作系统级别的行为(例如 I/O)都是在内核态处理的。同时 I/O 操作为了平衡主存和磁盘之间的读写速度以及保护磁盘写入次数,做了缓存处理,即 page cache该缓存是位于内核态主存中的。

内核态空间,用户进程是无法直接访问的,可以间接通过系统调用获取并拷贝到用户态空间进行读取。 即一次读操作的简化流程为:

  1. 用户进程发起读取数据操作read()

  2. read()通过系统调用函数调用内核态的函数读取数据

  3. 内核态会判断读取内存页是否在 Page Cache中,如果命中缓存,则直接拷贝到主存中供用户进程使用

  4. 如果未命中,则先从磁盘将数据按照 Page Size对齐拷贝到 Page Cache中,然后再次执行上面步骤 3

所以一次普通读写,最多需要经历两次数据拷贝,一次是从磁盘映射到 Page cache,第二次是Page Cachef拷贝到用户进程空间。

以上只是简化后的流程,对文件读写操作感兴趣的可以通过该文章学习从内核文件系统看文件读写过程

优缺点

由上可知 mmap相比普通的文件读写,优势在于可以有选择的映射,只加载一部分内容到进程虚拟内存中。另一方面,由于 mmap是直接映射磁盘文件到虚拟内存,减少了数据交换的次数,所以写入性能也更快。

在存在优势的同时,也有一些缺点,例如 mmap 要求加载的最小单位为 VM Page Size,所以如果是小文件,该方法会导致碎片空间浪费。

mmap API 示例

mmap 实际应用主要是 mmap() & munmap()两个函数实现。两个函数原型如下:

1
2
3
4
5
/// 需要导入头文件
#import <sys/mman.h>

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);

函数参数:

  • start:映射区的其实位置,设置为零表示由系统决定映射区的起始位置
  • length: 映射区长度,单位是字节, 不足一页内存按一整页处理
  • prot:期望的内存保护标志,不能与文件打开模式冲突,支持 | 取多个值
    • PROT_EXEC: 页内容允许执行
    • PROT_READ:页内容允许读取
    • PROT_WRITE:页内容可以写入
    • PROT_NONE:不可访问
  • flags:指定映射对象的类型,映射选项和映射页是否可以共享(这里只注释使用的两项,其他更多定义可以自行查看)
    • MAP_SHARED:与其它所有映射这个文件对象的进程共享映射空间。对共享区的写入,相当于输出到文件。
    • MAP_FILE:默认值,表示从文件中映射
  • fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明是匿名映射。
  • off_set:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应offset必须是分页大小的整数倍。

mmap 回写时机并不是实时的,调用 msync()或者munmap() 时会从内存中回写到文件,系统异常退出也会进行内容回写,不会导致日志数据丢失,所以特别适合日志文件写入。

Demo 可以参考开源库 OOMDetector 中的 HighSppedLogger 类的使用封装,有比较完整的映射、写入、读取、同步的代码封装,可直接使用。

注意事项

mmap 允许映射到内存中的大小大于文件的大小,最后一个内存页不被使用的空间将会清零。但是如果映射的虚拟内存过大,超过了文件实际占用的内存页数量,后续访问会抛出异常。

示例可以参考认真分析mmap:是什么 为什么 怎么用 中的情景二:

超出文件大小的虚拟内存区域,文件所在页的内存扔可以访问,超出所在页的访问会抛出 Signal 信号异常

参考

优秀博客

整理编辑:@我是熊大

本期优秀博客的主题为:iOS 内购。

1、iOS内购详解 – 来自掘金:QiShare

@我是熊大:本文是QiShare针对内购写的一篇文章,包含了内购前的准备、内购流程、恢复购买、内购掉单等内容。

2、iOS内购(IAP)自动续订订阅类型总结 – 来自简书:凡几多

@我是熊大:本文主要介绍自动订阅的相关情况。自定订阅与其他的购买不同,是比较复杂的一种情况。自定续期订阅类是有连续性的,其中还有免费试用期、促销期、宽限期的概念。用户还可以取消续订,恢复续订等,这无疑又增加了复杂性。

3、iOS项目技术还债之路《二》IAP掉单优化 – 来自掘金:njuxjy

@我是熊大:IAP调单一定是大多数开发者不可避免的问题,作者针对调单情况做了非常详细的总结,如果你也正有类似的问题,推荐阅读。

4、苹果iOS内购三步曲:App内退款、历史订单查询、绑定用户防掉单!— WWDC21 – 来自掘金:37手游iOS技术运营团队

@我是熊大:本文是基于WWDC21的总结,介绍了最新的内购情况,StoreKit 2的出现让内购更简单。惊喜的是:客户端已经支持用户退款了。

5、SwiftyStoreKit – 来自SwiftyStoreKit

@我是熊大:一个star高达5.9k的开源库,支持内购查询、购买、校验、结束交易等。api简洁易懂,能帮助你在项目中快速接入内购,美中不足的是不支持订单退订,这还需要自己开发。

见闻

这一周阅读/浏览到的有趣的资讯。

1、不止用手才能打字,用脸也行 – 来自爱范儿:邓 南

@远恒之义:作为一名程序员,如果有一个要在照顾刚出生婴儿时完成的编码需求,遇到这种情况你会怎么做?购买静音键盘的打字声音或许对于小婴儿来说还是太吵了,那么如何才能一边带孩子一边工作呢,国外一名程序员给出了他的答案:面部打字。

图片来自:YouTube 频道「Everything Is Hacked」

这款特殊的键盘名为「CheekyKeys」,开发采用了现代图像识别技术,利用 OpenCV 和 DLib 等工具跟踪用户脸部特定点的移动,打字识别的速度尚可。准备好电脑和摄像头,再学习一下摩斯密码,用户就可以开始尝试面部打字,非常锻炼脸部肌肉。

2、程序员延寿指南 – 来自 Github: geekan

@远恒之义:最近在逛 V 站的时候刷到一个帖子,作者分享了一些如何活得更久的方法,里面提到了一个术语 ACM:All-Cause Mortality(全因死亡率),它指的是一定时期内各种原因导致的总死亡人数与该人群同期平均人口数之比。如何稳健的活得更久,关键在于降低全因死亡率,按照文中的说法以下措施均能降低 ACM。

  • 固体:吃白肉、蔬果为主,多吃辣,多吃坚果,中量碳水、多吃植物蛋白

  • 液体:喝咖啡,喝牛奶,喝茶,少喝或不喝甜味饮料,戒酒或每周 100g 内

  • 光照:晒太阳

  • 运动:每周 3 次 45 分钟挥拍运动

  • 睡眠:每天睡 7 小时全因死亡率最低;且 10-12 点间最好

3、Flying Through Giga Berlin

这是特斯拉在 YouTube 发布的柏林工厂的航拍视频,国内也有人搬运,可以在 B 站查看。不同于室外的高空航拍,这是一次工厂车间内的穿梭拍摄。无人机经过车间的各个厂区,飞跃车床和机械臂,从板材定型到成品车完成的主要环节都有展示。这些高度机械化的画面,非常震撼。

4、Apple 微距摄影大赛获奖作品赏析

微距模式是 iPhone 13 Pro 和 iPhone 13 Pro Max 开始支持的一种专门用于拍摄事物细节的功能。本次摄影大赛也是针对这两款机型举办,下面是其中一副获奖作品,很难想象这是用手机拍摄出来的。一片简单的树叶都有这么丰富的纹理,可以感受到微距模式下的镜头细节表现能力真的非常强。

5、Web 3.0 漫游指南 2022【完整篇】

Web 3.0 是一个最近很热的概念,那什么是 Web 3.0 呢?作者用一个非常简单的类比,现实世界里的读书、写博客、出书,分别对应 Web1(只读)、Web2(读-写)和Web3(读-写-拥有)的区别。Web3 里的拥有不仅表示所属权,还有收益权,你有权获得它所带来的收益。听着这像是一个完美世界,这样一个世界离我们又有多远呢?大家可以通过这部小书获得答案。

学习资料

整理编辑:Mimosa

iOS 高性能app架构

地址https://github.com/dudongge/iOS_Architecture

该仓库是 Advanced iOS App Architecture (1st Edition) 的翻译本,对于译文修改了一些错别字,有 pdf 和 word 可以选择。本书主要讨论了在开发 App 的时候,代码在各种架构中的表现和细节的不同,讨论了各种架构的优缺点以及在 iOS 中,这些架构又有何特点和不同。

工具推荐

整理编辑:CoderStar

Quiver

地址https://yliansoft.com/#quiver

软件状态:免费

软件介绍

Quiver是为程序员打造的笔记本。它让您可以在一个笔记中轻松混合文本、代码、MarkdownLaTeX,使用出色的代码编辑器编辑代码,实时预览 MarkdownLaTeX,并通过全文搜索立即找到任何笔记。

包含MacOSiOS两端。

Quiver

iTab

地址https://www.itab.link/

软件状态:免费

软件介绍

一款浏览器空白 Tab 页定制工具,支持几乎所有主流浏览器。iTab 提供了很多模板,你可以自由地搭建属于自己的 Tab 页。同时配备备忘录,TODO 等功能,浏览器在平常的工作中使用频率非常高,综合体验下来,把一部分内容聚焦到浏览器中的 Tab 页确是一个提高效率的好方案。

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS 摸鱼周报 第五十期

iOS 摸鱼周报 第四十九期

iOS摸鱼周报 第四十八期

iOS摸鱼周报 第四十七期

iOS摸鱼周报 第五十期

iOS摸鱼周报 第五十期

本期概要

  • 话题:WWDC 22 Call to Code
  • 面试模块:事件响应与传递
  • 优秀博客:复习 iOS 的 rebase 和 bind
  • 学习资料:闲话 Swift 协程
  • 开发工具:AppleParty 是三七互娱旗下37手游 iOS 团队研发,实现快速操作 App Store Connect 后台的自动化 macOS 工具。

本期话题

WWDC 22 Call to Code

Apple 宣布了 WWDC 22 的相关事项,时间是 6 月 6 号到 10 号,形式还是线上播放。苹果一向喜欢玩彩蛋,我们可以尝试从这张图片里获取一些信息。图片主体是 Swift 图标,更准确的说应该是 SwiftUI 的图标,图标边缘透出的光亮有一种黎明到来,开启新篇章的感觉,所以很可能 SwiftUI 将迎来重大更新。就可联想的范围来说,什么样的更新才算重大呢,对标 Flutter,有没有可能支持全栈:Windows、Linux、Web 等平台?这个想法确实能配得上黎明到来,至于是否会实现,还是会有别的我们想不到的大更新,就让我们等待它的到来吧。

同时 Swift Student Challenge 将继续举办,学生们可以通过 Swift Playgrounds 创造有趣的项目。项目提交截止时间是 4 月 25 号,获奖者将获得 Apple 提供的一件 WWDC22 主题外套,一套定制的别针套装和一年的开发者会员资格。活动详情可以点击 Swift Student Challenge 查看。

面试解析

整理编辑:JY

事件响应与传递

当指尖触碰屏幕,触摸事件由触屏生成后如何传递到当前应用?

通过 IOKit.framework 事件发生,被封装为 IOHIDEvent对象,然后通过 mach port 转发到 SpringBoard(也就是桌面)。然后再通过mach port转发给当前 APP 的主线程,主线程RunloopSource1触发,Source1回调内部触发Source0回调Source0的回调内部将事件封装成UIEvent ,然后调用UIApplicationsendEventUIEvent传给了UIWindow

souce1回调方法: __IOHIDEventSystemClientQueueCallback()

souce0回调方法: __UIApplicationHandleEventQueue()

寻找最佳响应者,这个过程也就是hit-testing,确定了响应链,接下来就是传递事件。

如果事件没能找到能够响应的对象,最终会释放掉。Runloop 在事件处理完后也会睡眠等待下一次事件。

寻找事件的最佳响应者(Hit-Testing)

当 APP 接受到触摸事件后,会被放入到当前应用的一个事件队列中(先发生先执行),出队后,Application 首先将事件传递给当前应用最后显示的UIWindow,询问是否能够响应事件,若窗口能够响应事件,则向下传递子视图是否能响应事件,优先询问后添加的视图的子视图,如果视图没有能够响应的子视图了,则自身就是最合适的响应者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//3种状态无法响应事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
//触摸点若不在当前视图上则无法响应事件
if ([self pointInside:point withEvent:event] == NO) return nil;
//从后往前遍历子视图数组
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0; i--) {
// 获取子视图
UIView *childView = self.subviews[i];
// 坐标系的转换,把触摸点在当前视图上坐标转换为在子视图上的坐标
CGPoint childP = [self convertPoint:point toView:childView];
//询问子视图层级中的最佳响应视图
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView) {
//如果子视图中有更合适的就返回
return fitView;
}
}
//没有在子视图中找到更合适的响应视图,那么自身就是最合适的
return self;
}

传递事件

找到最佳响应者后开始传递事件

UIApplication sendEvent =>UIWindow sendEvent =>UIWindow _sendTouchesForEvent =>touchesBegin

UIApplication 是怎么知道要把事件传给哪个 window 的?window 又是怎么知道哪个视图才是最佳响应者的呢?

hit-testing过程中将 Windowview绑定在 UIEvent上的touch对象

响应者为什么能够处理响应事件,提供了哪些方法?

1
2
3
4
5
6
7
8
//手指触碰屏幕,触摸开始
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指在屏幕上移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指离开屏幕,触摸结束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//触摸结束前,某个系统事件中断了触摸,例如电话呼入
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

触摸事件如何沿着响应链流动?

在确定最佳响应者之后,优先给最佳的对象响应,如果最佳对象要将事件传递给其他响应者,这个从底到上的过程叫做响应链。

如果有 UIResponder、手势、UIControl 同时存在,是怎么处理的?

系统提供的有默认 action 操作的 UIControl,例如 UIButton、UISwitch 等的单击,响应优先级比手势高,而自定义的却比手势识别器要低,然后才是 UIResponder

Window 在将事件传递给 hit-tested view 之前,会先将事件传递给相关的手势识别器,并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消 hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view 才完全接手事件的响应权。

Window怎么知道要把事件传递给哪些手势识别器?

event 绑定的touch对象维护了一个手势数组,在 hit-testing 的过程中收集对应的手势识别器, Window 先将事件传递给这些手势识别器,再传给 hit-tested view。一旦有手势识别器成功识别了手势,Application 就会取消hit-tested view对事件的响应。

手势识别器与UIResponder对于事件响应的联系?

  • Window先将绑定了触摸对象的事件传递给触摸对象上绑定的手势识别器,再发送给触摸对象对应的 hit-tested view

  • 手势识别器识别手势期间,若触摸对象的触摸状态发生变化,事件都是先发送给手势识别器再发送给 hit-test view

  • 手势识别器若成功识别了手势,则通知 Application 取消 hit-tested view 对于事件的响应,并停止向 hit-tested view 发送事件;

  • 若手势识别器未能识别手势,而此时触摸并未结束,则停止向手势识别器发送事件,仅向 hit-test view 发送事件。

  • 若手势识别器未能识别手势,且此时触摸已经结束,则向 hit-tested view 发送 end 状态的 touch事件以停止对事件的响应。

cancelsTouchesInView 若设置成YES,则表示手势识别器在识别手势期间,截断事件,即不会将事件发送给hit-tested view。

delaysTouchesBegan 若设置成NO,则在手势识别失败时会立即通知Application发送状态为end的touch事件给hit-tested view以调用 touchesEnded:withEvent: 结束事件响应。

有哪些情况无法响应?

  • 不允许交互userInteractionEnabled = NO

  • 隐藏hidden = YES):如果父视图隐藏,那么子视图也会隐藏,隐藏的视图无法接收事件

  • 透明度:alpha < 0.01 如果设置一个视图的透明度<0.01,会直接影响子视图的透明度。alpha:0.0~0.01为透明。

参考

iOS触摸事件全家桶

优秀博客

整理编辑:皮拉夫大王在此

本期优秀博客主题为重新了解rebase & bind 。前段时间字节发了篇关于iOS 15fixup-chain机制的相关文章,其中rebase机制引起了大家热烈的讨论。在讨论的过程中,包括我在内的部分同学纠正了之前对rebase的错误认识,因此有必要跟大家一块再来学习下rebase & bind

在阅读之前,先来问几个问题:

  • reabse 时会修改TEXT段的数据吗?如果不修改,那静态链接时还不知道ASLR后的真实地址难道不需要通过rebase修正吗?如果要修改,TEXT段不是只读段吗,为什么可以修改呢?
  • iOS 15之前的fixup-chain机制与之前的rebase & bind有何不同?

如果你想真正了解rebase & bind机制,那么这两个问题要弄清楚。

1、复习iOS的rebase和bind

1.1 深入理解 Symbol – 来自公众号:小集

@皮拉夫大王:在了解rebase和bind之前必须要了解iOS的符号,符号是bind的桥梁。文章中对符号的介绍比较详细,包含之前很少提到的lazy symbol,weak symbol等。

1.2 给实习生讲明白 Lazy/Non-lazy Binding – 来自掘金:No

@皮拉夫大王:这篇文章是对bind讲解的浅显易懂,非常适合之前不了解bind的同学阅读。

1.3 图解 Mach-O 中的 got – 来自掘金:微微笑的蜗牛

@皮拉夫大王:这篇文章也是介绍相关知识的,可以补充阅读。

2、关于iOS15的fixup机制

2.1 iOS 15 如何让你的应用启动更快 – 来自掘金:ZacJi

@皮拉夫大王:iOS15的fixup介绍将主要通过三篇文章,逐次加深深度。阅读这篇文章后,大家应该要弄清楚作者所说的启动加速的原因,以及与二进制重排是否有关系。

2.2 从野指针探测到对iOS 15 bind 的探索 – 来自公众号:皮拉夫大王在此

@皮拉夫大王:在阅读了《iOS 15 如何让你的应用启动更快》,进一步探索了bind机制并且加以应用。

2.3 iOS15 动态链接 fixup chain 原理详解 – 来自公众号:字节跳动终端技术

@皮拉夫大王:更加完善地介绍iOS 15的fixup机制。

见闻

这一周阅读/浏览到的有趣的资讯。

1、识花软件-形色

@zhangferry:春天来了,又到了踏春赏花的时候,如果此时没有疫情可能大家的脚步能走的更远。春天的信号最明显的就是马路或者公园里盛开了各种各样的花,五颜六色的,非常招人喜欢。当我把手机对准这些花的时候,才发现他们在我眼里统称为花,即使相差很大,我也很难说这是什么花那是什么花。后来找到了一个叫做形色的软件,它可以通过拍照识别花的名字。我将小区和附近公园里的花用它来识别,于是就有了下面这张图:

终于知道这些花叫什么名字了,当我把花和它们各自的名字对应上之后,花也感觉更好看了。

2、互联网用户公众账号信息服务管理规定

@zhangferry:最近在公众号读到几篇疫情相关的文章,转发了一下,没多久就被平台删文了。在被删文的说明里附了一个链接,链接指向即是《互联网用户公众账号信息服务管理规定》,该规定的管理范围覆盖我们通过网络获取的几乎所有信息。我们需要理解公众信息生产的三方:平台、内容生产者和监管之间各自的职责和义务。内容不长,分五个章节,以下仅是概括,不属于解读。

第一章:总则。是整个规定的纲领,说明了公众账号信息服务涉及的三方(平台方、内容生产者、监督者)各自的职责。包含这些要点:

  • 国家网信部分负责该规定的监督、执法、管理工作。
  • 公众账号信息服务平台和公众账号生产运营者应当遵守法律法规,生产发布向上向善的优质信息内容。
  • 鼓励各级党政机关、企事业单位和人民团体注册运营公众账号,生产发布高质量政务信息或者公共服务信息,满足公众信息需求。(这也是各个社交媒体都能看到政务机关官方号的原因。)
  • 公众账号信息服务平台的运营需取得互联网新闻信息服务许可。

第二章:公众账号信息服务平台。这一章节内容最多,是对平台方的一些约束。包含这些要点:

  • 平台是信息生成内容的主题责任对象。
  • 需建立公众账号分类注册和分类生产制度、公众账号主体信息核验、注册限制、保证数据真实性等措施。

第三章:公众账号生产运营者。是对生成信息的一些约束。包含这些:

  • 如实填写账号注册信息。
  • 公众账号生产运营者应当履行信息内容生产和公众账号运营管理主体责任。
  • 遵守著作权保护相关法律法规。
  • 不得发布虚假、煽动用户情绪、不实、违法等信息。

第四章:监督管理。公共信息中参与的三方都需要的监督管理。

  • 平台。应加强对公众账号的管理,及时处理违法违规行为。
  • 内容生产者。也包括平台,应接受社会的监督
  • 各级网信部门。建立健全响应的工作制度。

第五章:附则。附加说明:

  • 互联网公众账号覆盖范围,在互联网中发布文字、图片、音视频等信息内容都包括。
  • 生效时间:2021 年 2 月 22 日起施行。

3、什么是CNAME以及CDN? – 来自知乎:漢堡再来一个

@zhangferry:前一段时间发现往 Gitee 上传图片失败,博客的图片也全挂了,打开邮箱发现 Gitee 发的一封邮件:

果然免费的东西不好用,简单调研之后决定迁移到七牛云上。

使用七牛云作为图床的话,需要用到它的两个服务:存储和访问(使用七牛云的前提是要有备案域名)。存储是每月免费 10 个 G。访问的话,默认开启 CDN 加速,这部分流量需要付费,这个可以根据需求购买对应的流量包,很便宜。这里可以讲下配置 CDN 加速时遇到的两个概念,CNAME 和 CDN。

CDN 的作用是访问加速,如何加速呢,就是分配多个服务器上,就近访问,访问之后该服务器会缓存源站的资源,之后的访问就不会请求源站而是直接访问这台就近的服务器了。

我们配置一个域名,例如 cdn.zhangferry.com,将它的源站指向七牛的存储空间。这里是一对一的关系,为了能够实现一对多,我们不直接指向源站,而是指向七牛的调度服务器。这个实现就是利用 CNAME,它相当于给我们需要解析的域名起一个别名,访问 cdn.zhangferry,就会访问到七牛调度服务器,这台服务器还可以配置 CNAME,再去指向另外一个域名。我们可以使用 dig 命令验证这个流程,cdn 域名的最终指向是一个特定的 IP 服务器,只不过在不同地区这个目标服务器 IP 不同。

4、GIF:一个观察互联网历史的切面 – 来自公众号:全媒派

@远恒之义:GIF 动图是数字时代的图像语言。GIF 生产方便,传播迅速,满足了人类交际的需求,刺激人们的视觉与情感,成了线上文化的迷因。当我们在斗图的时候,比的是表情包的资源,拼的是网上冲浪的时间,笑的是对“梗”文化的认同。不知道大家平时在社交媒体上使用表情包的频率如何,有没有想过 GIF 到底是怎么来的呢?

5、手中有粮,心里不慌:如何储备自己的物资「大后方」 – 来自少数派:乔淼

@远恒之义:最近吉林长春和上海疫情严重,身边有朋友身处这两地被困家中,暂时性的物质短缺。看到群里的小伙伴自己用黄豆发豆芽,心中感慨万千,不是滋味。

这是一份科学的囤货指南,总结了作者在物资储备方面的心得体会,满足短期与长期的食物存储需求。关于其他物资的储备以及培养个人储备习惯,作者在文章中也有明确的建议。希望大家都能成为生活的高手。

生存危机未必只发生在远离文明世界的地方。(各种突发事件)将切断所有的日常服务和食物供应。……在大城市,商店的食品架上将空空如也……公园和花园里的植物皮将会被剥光……(在正常的秩序恢复前)你只有依靠自身的资源条件和技巧安排生活。
———约翰·怀斯曼,《生存手册》,第 11 章 14 节,「大后方」

学习资料

整理编辑:Mimosa

闲话 Swift 协程

地址https://www.bennyhuo.com/book/swift-coroutines/

该系列博客从浅入深地介绍了 Swift 在 5.5 中新支持的协程特性。该系列文章介绍了 Swift 协程的特性,内容以 Swift 协程的基本概念、语法设计、使用场景等方面为基础展开,也会与大前端开发者常见的 Kotlin、JavaScript 做对比(作者是 Kotlin GDE),作者希望这个系列能给大家一个更多元化的视角来理解这个语法特性,十分推荐。

工具推荐

整理编辑:CoderStar

AppleParty

地址https://github.com/37iOS/AppleParty

软件状态:开源

软件介绍

介绍一个我们周报团队成员所在公司开源的一个项目:AppleParty

AppleParty 是三七互娱旗下37手游 iOS 团队研发,实现快速操作 App Store Connect 后台的自动化 macOS 工具。

支持功能:

  • 内购买项目管理(批量创建和更新);
  • 批量商店图和预览视频上传和更新;
  • 邮件发送工具;
  • 二维码扫描和生成工具;

AppleParty

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS 摸鱼周报 第四十九期

iOS摸鱼周报 第四十八期

iOS摸鱼周报 第四十七期

iOS摸鱼周报 第四十六期

iOS摸鱼周报 第四十九期

iOS摸鱼周报 第四十九期

本期概要

  • 话题:Chrom 100 发布,关于阅读器类的 App 审核指南有所更新
  • 面试模块:Runtime 中的 StripeMap 模板类
  • 优秀博客:Swift 5.6 和 Xcode 13.3 的新特性和新功能
  • 学习资料:即时设计是一款可以在线实时协作的专业 UI 设计工具
  • 开发工具:Decode,将 Xcode Interface Builder 文件(XibStoryboard 文件)转换为 Swift 源代码。

资讯

Google Chrome 发布到版本号 100

新版本更新不算大,将继续大幅减少内存、CPU占用率,速度会更快。本次更新应用图标也进行了更换,红黄绿相交边缘的阴影变得更小了。从图标的发展来看,Google 的设计风格越来越扁平化。

「阅读器」App 分发的更新

去年,Apple 宣布 了 2022 年初在 App Store 上将进行的更新,该更新将允许「阅读器」App 的开发者在 App 中提供一个指向其网站的链接,以便用户创建或管理帐户。从今天开始,《App Store 审核指南》中的准则 3.1.3(a) 将会更新,阐明阅读器 App 的开发者现在可以申请外部链接的帐户授权。

面试解析

整理编辑:Hello World

StripeMap<T> 模板类

StripeMap<T> 是 OC Runtime 中定义的一个类,用于引用计数表、Synchroinzed、以及属性设置时的 lock列表等。该类可以理解成是一种特殊的 hashmap。

特殊性体现在:一般的 hashmap key&value 是一一对应的,即使存在哈希冲突,也会通过其他方法解决该冲突,但是StripeMap是 key&value 多对一的。

我们去思考下 apple 为什么要将内存管理的表结构设计为 StripeMap 类型?先了解下简化后定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum { CacheLineSize = 64 };

template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize); // 对齐
};

PaddedT array[StripeCount];
};

*StripeMap存在的意义很显然就是优化访问性能 *. 优化高频访问 <T>产生的性能瓶颈,尤其是在多线程资源竞争场景下的。根据注释主要体现在以下方面

  • 对象结构内部维护一个数组,根据设备的不同分成不同的页,移动设备是 8 页,其他是 64 页。
  • 它使用对象的地址作为 key,进行哈希运算后获取一个 index,取得对应的 <T> value 。映射关系为 void* -> <T>
  • 做内存对齐,提高cpu 缓存命中率

分离锁优化

StripeMap 实质上是对分离锁概念的实现,简单概述下个人对分离锁的理解
我们都知道多线程场景下,如果多个变量都会被多线程访问和修改,最好的办法是针对不同的变量用不同的锁对象来实现资源管理。这样可以避免访问一个变量时,多线程访问其他不相关的变量时被阻塞等待。这其实是对分拆锁的应用。即避免对同一个锁访问等待。
而分离锁则是对上述思路的进一步优化,针对同一个高频访问的对象来说,分段管理可以解决线程之间的资源竞争。拿 SideTables举例来说:
SideTables 表结构分拆为 8 份,每一份维护一个锁对象。这样在高频访问时,在保证线程安全的同时最多可以支持访问所有的 8 页表数据。实现思路是数组 + 哈希函数(将地址指针转换为 index 索引)。

1
2
3
4
5
6
typedef unsigned long uintptr_t;

static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}

将地址指针强制转换为 uintptr_t 无符号长整型,uintptr_t 定义为 8 字节,保证了指针(8 字节)的全量转换不会溢出。
然后通过位运算以及取模保证 index 落在 StripeCount 索引范围内。

CPU Cache Line

在上面优化方式第三条提到,在定义模板类型 <T> 时,使用 CacheLineSize做了内存对齐。
通常来讲,内存对齐的目的是为了加快 CPU 的访问,这里也不例外,

但是好奇的是 OC 中常见的内存对齐大小一般是对象的 16 字节对齐。而 StripedMap 定义了 64 字节对齐是出于什么考虑。

这里直接给出结论(理论部分不感兴趣的同学可以直接跳过):CacheLine 是 CPU Cache 缓存和主内存一次交换数据的大小,不同 CPU 上不同,Mac & iOS 上是 64 字节,这里是为了解决 Cpu Cache中的伪共享(False Sharing)问题。

出于探索心理,搜索了一下关键字。因为笔者对操作系统了解不多,所以只是做一个概述:

  1. CPU 和内存之间由于存在巨大的频率差距,影响数据访问速度从而诞生了 CPU Cache 的概念,Cache LineCPU Cache之间数据传输和操作的最小单位。意味着每一次缓存之间的数据交换都是Cache Line的倍数。这是前置条件。

  2. 另外一个重要的点是不同核心之间 L1 和 L2 缓存是不共享的,其他核心中的线程要访问当前核心缓存中的数据需要发送 RFO 消息,当前核心重置命中的的Cache Line状态,并经过一次 L1 / L2 => L3/主内存的数据写入,另外一个核心再次读取后才能访问。如果频繁的在两个核心线程中访问。会造成性能损耗。

我们假设一个场景, 两个不相关的变量 var1var2 小于 64 字节,并且内存中紧邻,这时一个核心加载了包含 var1 和 var2 内存区域的Cache Line 并更新了 var1的值,此时处于另外一个核心需要访问 var2就会出现上面第二条的情况。
解决这类问题的思路就是空间换时间。也是 StripedMap 的做法。内存对齐,尽量保证不同页的 SideTable表结构会在不同的 Cahce Line上。这样不同的核心线程就可以做到同时处理两个变量值。

优秀博客

本期将整理一些有关最近发布的 Swift 5.6 和 Xcode 13.3 的新特性和新功能的博文。

整理编辑:东坡肘子

1、Swift 5.6 新特性 – 来自掘金:YungFan

@东坡肘子:Swift 5.6 为该语言引入了一系列新的特性,同时为了协助开发者顺利地过渡至 Swift 6 而完善了部分其他功能。本文对 Swift 5.6 的新特性进行了介绍。另外,在 Swift 每次升级后,hackingwithswift.com 都会有专文介绍新添加的内容,感兴趣的朋友可以持续关注。

2、Swift 5.6 SE-0335 Introduce existential any 的理解 – 来自掘金:赤脊山的豺狼人

@东坡肘子:在Swift 5.6 的新功能中,引入了一个新的关键字 any 来标记 existential type。这种变化有可能在未来破坏现有代码,使其在 Swift 6 中会出现兼容性问题。所有的 Swift 开发者都应及时对其有所了解。本文的作者将对 any 的用法和存在的理由进行介绍和探讨。

3、在GitHub页面上将DocC文档作为静态网站发布 – 来自:Moritz Philip Recke

@东坡肘子:Swift 5.6 为 Swift Package Manager 实现了可扩展的构建工具(SE-0303)及其扩展命令插件(SE-0332)等功能。本教程展示了如何利用新功能生成 DocC 文档,并将其处理为可静态托管的文档。

4、Xcode 13.3 添加了在私有存储库中使用 SPM 二进制依赖项的能力 – 来自:Marco Eidinger

@东坡肘子:无法通过二进制发布库是阻碍开发者全面转向 Swift Package Manager 的一个重要因素。在 Xcode 12 中添加了XCFramework ,使得二进制发布得以可能。Xcode 13.3 再接再厉,增加了 XCFramework 对私有存储库的支持。开发者通过在开发私有的闭源库时将其代码作为二进制文件提供,以进一步保护其知识产权。

5、Swift异步算法介绍 – 来自:Tony Parker

@东坡肘子:Swift 5.5 为 Swift 带来了全新的异步开发体验。近日,苹果公开了跨平台开源项目 Swift Async Algorithms,为开发者提供了更加自然、高效地处理异步序列的能力。该库需要 Swift 5.6 的支持。

见闻

这一周阅读/浏览到的有趣的资讯。

1、潘爱民:计算机程序的演进——我的程序人生三十年 – 来自公众号:CSDN

@zhangferry:本篇文章是潘爱民的自述,程序人生的三十年也对应于计算机行业发展的三十年。从代码运行方式、网络、人工智能、交互形态这几个维度的发展做了一些总结,文末对程序技术的未来做了一波预测。

其中提到了一个名词,数字孪生,「这是一个物联网概念,指在信息化平台内模拟物理实体、流程或者系统,类似实体系统在信息化平台中的双胞胎。借助于数字映射,可以在信息化平台上了解物理实体的状态,甚至可以对物理实体里面预定义的接口组件进行控制。」其对应的正是人们对于未来的想象,这一场景的发展还属于探索期。

文中有一句也挺让人动容的:我始终认为,程序员写代码是一种创造活动,这是程序员职业的神圣之处。而且还是出自一位在计算机行业奋斗了三十年的”大龄程序员“。曾几何时我也怀揣这种想法,但经历几年职场的摸爬滚打和大环境不断有人发出认清现实的呼喊,我感觉自己也被侵染的认为抛弃理想谈论现实才是一种成熟,自以为的认清了现实,是不是反而是背道而驰呢?

2、中国第一代程序员潘爱民的 30 年程序人生 – 来自公众号:CSDN

@zhangferry:因为最近也在看潘爱民参与编写的《程序员修炼之道–链接、装载与库》,就又找了几篇跟他有关的文章。这一篇也是自述,主要讲他的职业经历,从微软亚洲研究院、盛大创新院、阿里巴巴到创业杭州指令集。我比较感兴趣的是潘爱民在阿里参与 YunOS 的经历(2013年),这份工作的机缘巧合还要往前推到亚洲研究院时。

我从 Windows 性能诊断分析作为切入点,研究 Windows 的内部机理,将 Windows 线程调度、内存管理、I/O 等最核心的模块剖析了一遍,并形成了一套系统性的诊断方法。有了这些基础以后,我又进一步考虑应用层的性能问题,以浏览器的渲染引擎作为研究对象,分析渲染引擎的整个计算过程,挖掘可优化的空间。核心的思想是,在计算流程中尽可能把重复的计算移除掉,从而保持整个响应过程的高效。这些研究工作为我后来做操作系统打下了扎实的基础。

再之后在盛大创新院设计一个新的移动操作系统 VisionOS,虽然失败了但也积累了很多经验,以至于到阿里做YunOS 时,「我一心想做成云 OS」。最终 YunOS 也没成功,结果不免让人感慨。但回看潘爱民开挂的人生,其中非常关键的节点就是在亚洲研究院深入研究 Windows 这段经历,这种坚实的基础为其以后所从事的所有工作都提供了巨大帮助。

3、Variflight 全球航班实时跟踪雷达

@zhangferry:这个网站可以查看全球航班的实时飞行数据,且每隔几秒就会更新一次,那这些数据是如何获取的呢。注意到网站顶部有一个字母缩写:ADS-B,它的全称是:Automatic dependent surveillance – broadcast,广播式自助相关监视

它是一种飞机监视技术,飞机通过卫星导航系统确定其位置,并进行定期广播,使其可被追踪。该广播不需要人为操作,而是作为一种基础功能自动定时触发,新一代的飞机会被强要求配备该设备。它包含 ADS-B Out 和 ADS-B Int 两项服务,前者用于广播信息,后者用于接收信息。它有两个好处,一个是空中交通管制在想确认飞机信息时可以不用问询直接查看,二是可以空中采集其他飞机发出的信息,进行自主规避。

因为广播的性质,这类信息的获取相对容易,像是 VarFlight、FlightAware 这类网站就是基于这些信息的聚合做出上述航班跟踪系统的。

4、DecoHack - 独立开发者的灵感周刊

@zhangferry:一份面向独立开发者,帮助他们发现新产品新方向的一份周刊,由一位腾讯的设计师创建,目前已经出到第 7 期。在这上面能发现很多小众却很精美的应用,以供开发者寻找灵感;还会分享一些技术教程、开公司所需处理的税务、营销技巧等内容。

在这个周刊里还发现了一个很有趣的网站:https://lofi.co/,它可以模拟咖啡厅、书店的场景,并播放一些白噪音。在家远程办公,开着它往那一放,就很舒服。

学习资料

整理编辑:Mimosa

即时教程

地址https://js.design/courses

即时设计社区组织的精选设计课程,即时设计是一款可以在线实时协作的专业 UI 设计工具,类似 Figma。在即时教程中你可以找到来自各大视频网站平台创作者们的免费高质量课程。从零基础开始一步步到做案例,进阶技巧,应有尽有,非常适合想学一点 UI 知识的程序员们。

工具推荐

整理编辑:CoderStar

Decode

地址https://microcodingapps.com/products/decode.html

软件状态:$8.99

软件介绍

Xcode Interface Builder 文件(XibStoryboard 文件)转换为 Swift 源代码。

Decode

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第四十八期

iOS摸鱼周报 第四十七期

iOS摸鱼周报 第四十六期

iOS摸鱼周报 第四十五期

iOS 摸鱼周报 第四十八期

iOS 摸鱼周报 第四十八期

本期概要

  • 话题:node-ipc 供应链投毒事件
  • 面试模块:OC 对象弱引用指针标识位
  • 优秀博客:程序员如何自我提升
  • 学习资料:以 Java 为背景的全栈知识体系
  • 开发工具:新一代卡片笔记工具:flomo

本期话题

@zhangferry:这期稍微聊一聊 「node-ipc 包以反战为名进行供应链投毒」这件事。这件事的原委是这样的,node-ipc 是 npm 下的一个组件(iOS 开发可以将其理解为 CocoaPods 下的一个组件),其作者为了表达反战宣言,在该组件库里注入了恶意脚本,往用户的桌面和 OneDrive 里写一个文件,用于表达自己的政治观点。供应链的含义是你发布的软件所依赖的三方库、系统库、开发工具等组成的依赖链,你的软件属于其中一环,它受以上所有环节的影响。而供应链投毒的含义是,只要依赖链里有它,就会中招,其中就包括使用很广泛的 vue-cli 。

当前国内 nmp 镜像已经将 node-ipc 列入黑名单,该作者推特也遭黑客攻击,个人信息被人肉。

这件事算是结束了,但也暴露出开源社区的脆弱,谴责该作者之时,「我们需要建立一种开源世界的反分裂共识」,开源社区的规则不应该被政治因素打破。

面试解析

整理编辑:Hello World

OC 对象如何知道存在关联的弱引用指针

我们都知道在释放对象之前会检查是否存在弱引用指针, 而判断 OC 对象存在弱引用的依据是什么呢?

如果卷过八股文,肯定了解 isa 优化过后使用了 union存储更多的数据,其中有一个 bit:weakly_referenced是和弱引用指针相关的。

在弱引用对象创建成功后,会去设置该位的值为 1。结构如下:

1
2
3
4
5
6
7
8
9
10
#     define ISA_BITFIELD                                                      \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19

但是未优化的 isa存储的是类对象的内存地址,不能存储弱引用信息, 那么它关联的弱引用信息应该存储在哪?答案是 引用计数表

在学习内存管理 release & retain流程时,发现引用计数表都是通过 SIDE_TABLE_RC_ONE 进行增减操作的。并未直接获取到引用计数后进行 +/- 1。该掩码定义处还给出了其他的定义:

1
2
3
#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
#define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit
#define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit

从定义大概猜到,引用计数表中获取到的数值,从第三位开始是真正的引用计数。第一位是用来表示是否存在弱引用指针的。第二位表示正在析构中。

我们在 weak创建流程中的关键函数 storeWeak中可以证实这一点,该函数在操作完弱引用表之后, 会设置对象的相关弱引用标识位,具体函数是setWeaklyReferenced_nolock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
inline void
objc_object::setWeaklyReferenced_nolock()
{
isa_t newisa, oldisa = LoadExclusive(&isa.bits);
do {
newisa = oldisa;
// 未优化的 isa
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
sidetable_setWeaklyReferenced_nolock();
return;
}

// 优化过的 isa
if (newisa.weakly_referenced) {
ClearExclusive(&isa.bits);
return;
}
newisa.weakly_referenced = true;
} while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
}

// 引用技术表中设置标识位
void
objc_object::sidetable_setWeaklyReferenced_nolock()
{
#if SUPPORT_NONPOINTER_ISA
ASSERT(!isa.nonpointer);
#endif

SideTable& table = SideTables()[this];

table.refcnts[this] |= SIDE_TABLE_WEAKLY_REFERENCED;
}

setWeaklyReferenced_nolock判断如果是优化过的 isa 直接设置对应的 weakly_referenced = 1

如果是非优化的 isa,则通过查找引用计数表设置对应的位置为 1。

在对象释放过程中,查找对象关联弱引用的逻辑具体实现在 objc_object::clearDeallocating()中,如果判断是优化后 isa则调用 clearDeallocating_slow查找 isa.weakly_referenced;如果是未优化 isa 则调用 objc_object::sidetable_clearDeallocating()查找,可自行查看。

另外关于 swift 弱引用可以学习 周报四十五期

优秀博客

整理编辑:@我是熊大

本期优秀博客的主题为:程序员如何自我提升。学习前辈们的经验,找到适合自己的路径。

1、程序员如何在业余时间提升自己 – 来自掘金:阿里巴巴大淘宝技术

@我是熊大:工作本身就很忙碌,如何在繁忙的工作中利用碎片化时间学习或是做自己感兴趣的事情,来自4名淘系技术的工程师的分享。

2、阿里毕玄:程序员如何提升自己的硬实力 – 来自segmentfault:阿里云云栖号

@我是熊大:作者从生物专业转到程序员,从业余程序员到职业程序员。

3、如何提升你的能力?给年轻程序员的几条建议 – 来自Glow 技术团队博客

@我是熊大:作者前后服务于NVIDIA、Google、Slide、Glow。在Glow,作者的个人的工作也从Developer,Tech Lead,Engineering Manager到CTO,他的看法可能会更全面。

4、程序员一定会有35岁危机吗 – 来自掘金:黄轶

@我是熊大:一个资深架构师的分享,正如他所说,企业并不是排斥大龄程序员,而是排斥能力与自己工龄不匹配的大龄程序员。

见闻

这一周阅读/浏览到的有趣的资讯。

1、开源世界里的法律与政治 – 来自博客:庄表伟

@zhangferry:文中有两个观点值得思考:

  • 如果这个世界可能被割裂,无论代码仓库放在哪里,整个世界都会受到伤害。所以关键不是自己也搞一个。而是要努力建设不会被割裂的开源世界。

    我不是太认同,我认为自己搞和努力建设更好的开源世界要同时进行,因为前者更可控,为了避免陷入未来两难的境地,还要好好搞。

  • 可以在个人账号发表政治观点,但不要代表开源社区,开源社区应该是”非政治“的。

2、Facebook 工程师文化独特之处 – 来自博客:Cat in Chinese

@zhangferry:作者讲述了他在 Facebook 工作 7 年 体会到的 Facebook 与其他公司的区别。

  • 工程师要对产品结果负责。把技术做到极致是不够的,产品完成指定目标才行。可能很多人会感觉奇怪,但这样能部门上下目标一致,不会出现甩锅的情况。
  • 基础架构被视为内部产品。比如某个部门产出了一个新的服务,你不能强行推进大家使用,而应把它当做一个产品,只不过用户是内部用户。这其中工程师还需要兼做销售和客服的工作。当初 React 和 React Native 早期就是经历了很多推广困难才得以成功。
  • 救火比防火更容易获得回报。这个是缺点,因为完全的数据驱动,这导致防御性措施很难吸引人去做,因为成功阻止了坏事发生时你没办法收集数据说你成功阻止了多少件坏事,而对于解决问题你可以明确的列出指标。

3、超越心流 – 来自播客:不可理喻

@zhangferry:「心流(Flow)」由心理学家米哈里-契克森米哈赖提出,它描述的是当一个人全神贯注的投入一件事情的时候,他全部的精神能量都专注于实现这个目标,心灵状态达到了一种最纯粹的、最优化的、最忘我的状态。「心流」代表着一种最优体验,它应该是我们追求的状态,但它非常依赖注意力,如何才能超越心流呢,作者结合了多个例子进行说明。

其中提到 The Well-Played Game 的作者对于真正的乐趣和纯粹的玩的定义,有几个标准:

  • 人要不断的制造惊喜,制造幽默感,哪怕你在做一个重复的无趣的事情。除了务实的你,还要有一个调皮捣蛋,不断给出惊喜,带着玩乐心态的自己。(这一条跟我某些体验比较像)
  • 要有玩的集体感,不要有竞争感和情绪化,而是考虑大家一起创造的游戏体验。
  • 好好的去玩,要介于有目的性和无目的性之间,既要领悟,还要有神秘感。

但理论毕竟是理论,个体感受是复杂的,很难定义标准,我们还应该结合自己的方式体会生活的快乐。

4、【亦】唠唠苹果 M1 Ultra:半导体新时代! – 来自BiliBili:林亦LYi

@zhangferry:不同于 M1 Ultra 的芯片测评,这期节目更多讲的是 M1 Ultra 的出现对半导体行业的影响。半导体行业有摩尔定律:当价格不变时,集成电路上可容纳的晶体管数目,约每隔 18 个月便会增加一倍,性能也将提升一倍。芯片制程从 5nm 到 3nm,摩尔定律还在生效,但它的物理极限也快到了。M1 Ultra 使用新一代缝合技术,在制程不变的情况下,靠两个芯片的拼接就完成了性能翻倍。而且这玩意可没有物理极限,这种依靠「缝合」技术来让性能翻倍带来的则是「摩尔定律 2.0 时代」。

还有一点很有趣的地方,苹果对于 UltraFusion 的专利描述如下:

img

在晶圆上排满 M1 Max 晶片,把相邻且联通达标的晶片找出来搭建信号通道,连上之后切割,作为M1 Ultra。对于跨电路通信有问题的晶片就单独拆成 M1 Max 来卖,M1 Max 来很复杂,万一也做坏了,可以横着来一刀变成 M1 Pro。M1 Pro 虽然没法再砍一刀变成 M1,但芯片里的 CPU、GPU 等还都可以复用到 M1 上(知道为啥 iPad 也上 M1 了吧。。) 。不得不说,苹果的这套设计确实强,这样不仅使得芯片造出来的良品很多,而且各种边角料都能复用,最大限度平摊芯片制造成本。

学习资料

整理编辑:Mimosa

Java 全栈知识体系

地址https://pdai.tech/

以 Java 开发为背景的全栈开发知识体系,内容包含软件开发、算法、面试、架构、项目、产品团队以及一些方法论的思考。站内资源海量详实,文章和网站的排版和设计很规范,阅读起来非常舒适,也多有漂亮的示意图来帮助读者理解,内容非常丰富。关于这个网站的建立初衷以及介绍可以看这里

工具推荐

整理编辑:CoderStar

flomo

地址https://flomoapp.com/

软件状态:免费

软件介绍

flomo 是新一代卡片笔记工具,秉承尼克拉斯 · 卢曼(Niklas Luhmann)的卡片笔记法理念,让你能更好的利用碎片时间积累知识,建立知识间的关联。

flomo

MoneyProgress

地址https://github.com/Lakr233/MoneyProgress

软件状态:免费

软件介绍

老王的又一力作:钱条。

上班的进度条,开始搬砖吧。

MoneyProgress

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS 成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第四十七期

iOS摸鱼周报 第四十六期

iOS摸鱼周报 第四十五期

iOS摸鱼周报 第四十四期

iOS摸鱼周报 第四十七期

iOS摸鱼周报 第四十七期

本期概要

  • 话题:苹果多个产品线的更新介绍
  • 面试模块:动态库与静态库的区别
  • 优秀博客:关于该不该换工作以及如何准备面试
  • 见闻:一个新的、偏技术领域的博客推荐模块
  • 学习资料:Rust 数据结构与算法
  • 开发工具:Aria2GUI,一款支持多种协议的轻量级命令行下载工具

本期话题

@zhangferry:苹果的多个产品线带来了一波更新。

macOS Monterey 12.3

  • Python 2 被从系统中移出了,但新系统中也并没有预装 Python 3,需要开发者手动安装。
  • Universal Control(通用控制):键盘、鼠标和触摸板可以在 Mac 和 iPad (iPadOS 15.4) 端无缝衔接。
  • M1 芯片的电脑可以搭配支持空间音频的 AirPods 使用头部追踪功能。

iOS 15.4

  • 支持戴口罩的 FaceID 功能,仅支持 iPhone 12 及之后的机型。
  • 新增了 37 个 Emoji 表情。

Xcode 13.3

  • 新增了一项针对 Swift 的编译优化
1
defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1
  • Instruments 获得了多项提升,提高了 leaks 、memory graph debugger 扫描的准确性等。

问题收集:

  • 反馈打包时有 pod 相关异常
  • 反馈编译变慢

Swift 5.6 Released

还有一个小优化:SE-0290: Unavailability Condition

面试解析

整理编辑:JY

静态库和动态库的区别

静态库(Static Library)

特点如下:

  • 分发文件大

  • 静态库默认仅将有用到的类文件 linkMach-O 中 (以类文件为最小链接单位)

  • ipa 包小(为了 App 瘦身,尽量将代码放静态库中)

    • 静态库中某个目标文件的代码没有被任何地方引用,则这个目标文件不会被链接到可执行文件中去(分类代码经常被优化掉,一般都使用 -Objc-all_load 或者 -force_load 来处理静态库分类加载问题)
  • App 冷启动速度快

    • 前提是不使用 动态库拆分 搭配 动态库懒加载方案
    • App 启动流程中有 rebasebind,多个静态库只需要 rebasebind 一次
  • 存在符号冲突可能

  • 共享 TEXT 段

    • iOS 9 以前单个 Mach-O 的 TEXT 限制 60M
    • iOS 9 以后单个 Mach-O 的 TEXT 限制 500M
  • 不需要额外签名验证

  • 静态库符号的可见性可以在链接期间被修改

  • 文件格式多为 fat 格式的静态库文件

  • 形式多为 .a.framework

  • 静态库不含 bitcode 时,引用静态库的目标部署时就不能包含 bitcode

动态库(Dynamic Library)

特点如下:

  • 分发文件小

  • ipa 包大(前提是不考虑懒加载的情况)

    • 动态库会把整个 lib 复制进 ipa
  • App 冷启动速度慢

    • App 启动流程中有 rebasebind,多个动态库只需要多次 rebasebind
  • 需要设置合适的 runpath

  • 需要动态加载

  • 需要签名且需要验证签名

    • 会检查 framework 的签名,签名中必须包含 TeamIdentifier,并且 framework 和 host App 的 TeamIdentifier 必须一致
    • Xcode 重签名,保证动态库签名一致性
  • 需要导出符号

  • 重复的 arch 结构

  • App 与动态库中重复代码可以共存,不会发生符号冲突

    • 因为可执行文件在构建链接阶段,遇到静态库则吸附进来,遇到动态库则打个标记,彼此保持独立性。
    • 对于来自动态库的符号,编译器会打个标记,交给 dyld 去加载和链接符号,也就是把链接的过程推迟到了运行时执行。(比如 App 使用的是 3.0 版本 SDK,动态库使用的是 1.0 版本 SDK,能正常运行,但是会有风险)
  • 链接后需要包含分发大小

  • 冷启动过程中,默认会在 main 函数之前加载

    • 默认情况下,过多的动态库会拖慢冷启动速度
    • 如果采用懒加载动态库的形式,能够加快 App 的启动速度,可以使用 dlopenbundle 懒加载优化
  • 文件格式 Mach-O(一个没有 main 函数的可执行文件)

  • 动态库不包含 bitcode 时,引用动态库的目标部署时可以包含 bitcode

  • CocoaPodsv0.36.0 开始,可添加关键字 use_frameworks! 编译成类似 Embedded Framework 的结构(可以称之为 umbrella framework

    • 缺点:默认把项目的依赖全部改为动态库(可使用 use_modular_headers!,也可以在 podsepc 添加 s.static_framework = true 规避)
    • CocoaPods 执行脚本把动态库嵌入到 .appFramework 目录下(相当于在 Embedded Binaries 加入动态库)

优秀博客

整理编辑:皮拉夫大王在此

本期优秀博客主题相对轻松,聊聊面试相关和成长相关的事情。本来想借助本期内容整理下 rebase & bind 的相关技术细节,但是这周被某些自媒体散布的裁员消息给刷屏了,恰巧我本人也是在最近换了工作,因此借助这个机会和大家一起暂停下技术学习的脚步,抬头看看外面的情况。

阅读后你将获得什么?

  • 如果你在犹豫自己是不是该换工作,那么可以从文章中找到部分答案;
  • 东野浪子和苍耳两位大佬是非常资深的大厂面试官,他们的建议是非常中肯的;
  • 我本人近期面试的一些细节;
  • 尽管不认同面试问八股文,但是还是给大家准备了八股集合,以供大家增强面试信心;

1、如果你在犹豫期,请看下文

1.1 浅谈如何理性的判断自己是否应该换工作 – 来公众号:东野职场派

@皮拉夫大王:去年推荐过这篇文章,考虑到目前是金三银四,有些同学可能之前没有看过,因此再推荐一次。

2、面试官篇:知己知彼,面试官的关注点

2.1 给面试者的一些建议 – 来自:苍耳的技术博客

2.2 面试过500+位候选人之后,想谈谈面试官视角的一些期待 – 来公众号:东野职场派

@皮拉夫大王:以上两篇文章的观点本质上来说是一致的,面试官期望候选人是在平时工作中是有所思考和行动的人,而不是临时抱佛脚去应试。因此用半年时间去刷题复习基础知识,不如用这个时间去认真打磨一个项目

3、候选人篇:近期面试的一些细节

3.1 刚换工作,说点找工作相关的事情~ – 来自公众号:皮拉夫大王在此

@皮拉夫大王:这是我本人近期的亲身经历,前段时间和几个朋友聊了聊换工作的事情。包括:该走该留?如何准备?如何写简历?如何投简历?面试中和面试后各有哪些问题?等等

4、最全基础知识整理

4.1 《史上最全iOS八股文面试题》2022年 – 来自51CTO:宇夜iOS

@皮拉夫大王:面试中多多少少会考察到部分基础知识,对基础不放心的同学可以看看。

见闻

整理编辑:zhangferry

这一周阅读或者观看到的有价值的讯息。

1、深度学习撞墙了 – 来自:机器之心

@zhangferry:早在 2016 年,深度学习教父级人物 Hinton 就曾说过,我们不用再培养放射科医生了。但如今 AI 并没有取代任何一位放射科医生,问题出在哪呢?在 Robust.AI 创始人 Gary Marcus 看来深度学习可能就要撞墙了。整个 AI 领域需要寻找新的出路。

深度学习本质上是一种识别模式,当我们只需粗略结果时,它非常适合,但是对于需要精确性操作且风险很高的事情,像放射学和无人驾驶,就需要很谨慎了。人工智能确实没有我们想象的进化那么快,所以它的未来是悲观的吗?并不是,作者提出 Hinton 这样的先驱把深度学习的研究方向带偏了,应当将深度学习和符号处理结合起来,这种混合人工智能可能才是最好的方向。

2、【译文】谷歌搜索正在消亡 – 来自少数派:赵喧典

作者认为 reddit 才是目前最受欢迎的搜索引擎,而谷歌搜索正在走向消亡。认为谷歌不再被认可的原因有这几个:

  • 广告:谷歌的大部分收入来源于广告,但过多广告占据搜索词条会严重影响用户体验。
  • SEO 优化:很多人的工作就是搜索引擎优化,这违背了公平也会导致搜索质量下降。
  • 人工智能:人工智能在尝试帮你找到你想要的内容,但这种揣测经常让人不满意。

3、领导,我想改善团队的分享氛围 – 来自公众号:hockor

@zhangferry:大多数人都会在工作中遇到技术分享这个事情,作为 TL 应该如何打造良好的分享氛围呢?首先明确良好的分享氛围是有很大好处的,比如提升团队的技术视野、发现团队牛人、提升团队战斗力、扩大团队影响力等。分享形式较普遍的定期举行技术分享会,任何的分享行为都应该被鼓励。“分享本身是一种精神上自我实现的行为,所以无论分享内容如何,至少这种行为是慷慨的,我们应该及时的、积极的反馈,去鼓励他们往前更进一步”。

同时作为分享的参与者,我们应该抱着探索者的积极的心态去听,有参与感的学习形式是非常高效的。

4、Usage statistics of content languages for websites – 来自网站:W3Techs

@zhangferry:当前世界上的网站按语言划分的话,英语最多,这个毋庸置疑。但第二多的竟然是俄语,更令人意外的是,作为使用人口非常多的汉语,其网站数量占比竟然排到了第 10 位。我能想到的原因是,俄语地区互联网发展比较早,催生了很多网站;汉语虽然使用人数多,但是相对集中,国内互联网的发展比较晚,近几年移动互联网浪潮催生了很多 App,但网站的创建则很少。

网站是目前人们获取信息最重要的途径之一,英语网站远超其他语种,也反应了当前英语世界的话语权是更大的。

学习资料

整理编辑:Mimosa

Rust 数据结构与算法

地址https://github.com/QMHTMY/RustBook

一本 Rust 书籍,有简体和繁体版(英文版和日文版正在撰写中),内容包括算法分析,基本数据结构和算法,外加一些实战,共有九章。包含了大家常用的常见的数据结构的实现和讲解,配有详实的代码和清晰简明的图解。

工具推荐

整理编辑:CoderStar

Aria2GUI

地址https://github.com/yangshun1029/aria2gui

软件状态:免费

软件介绍

Aria2GUI 是一款支持多种协议的轻量级命令行下载工具,可以轻松的下载离线资源。

Aria2GUI

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第四十六期

iOS摸鱼周报 第四十五期

iOS摸鱼周报 第四十四期

iOS摸鱼周报 第四十三期