Skip to content

Commit b5ef80b

Browse files
author
ClownFish
committedApr 21, 2022
执行分类脚本
1 parent 57a4be5 commit b5ef80b

File tree

7 files changed

+335
-3
lines changed

7 files changed

+335
-3
lines changed
 

‎CategorySummary/Articles.md

+66
Original file line numberDiff line numberDiff line change
@@ -1421,3 +1421,69 @@ Vision 是苹果在 WWDC 2017 推出的图像识别框架。与 Core Image、AV
14211421

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

1424+
***
1425+
整理编辑:皮拉夫大王在此
1426+
1427+
> 本期优秀博客主题为重新了解`rebase` & ` bind` 。前段时间字节发了篇关于iOS 15`fixup-chain`机制的相关文章,其中`rebase`机制引起了大家热烈的讨论。在讨论的过程中,包括我在内的部分同学纠正了之前对`rebase`的错误认识,因此有必要跟大家一块再来学习下`rebase` & ` bind`
1428+
>
1429+
> **在阅读之前,先来问几个问题:**
1430+
>
1431+
> - `reabse` 时会修改TEXT段的数据吗?如果不修改,那静态链接时还不知道ASLR后的真实地址难道不需要通过rebase修正吗?如果要修改,TEXT段不是只读段吗,为什么可以修改呢?
1432+
> - iOS 15之前的`fixup-chain`机制与之前的`rebase` & ` bind`有何不同?
1433+
>
1434+
> 如果你想真正了解`rebase` & ` bind`机制,那么这两个问题要弄清楚。
1435+
1436+
1、**复习iOS的rebase和bind**
1437+
1438+
1.1 [深入理解 Symbol](https://mp.weixin.qq.com/s/uss-RFgWhIIPc6JPqymsNg) -- 来自公众号:小集
1439+
1440+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):在了解rebase和bind之前必须要了解iOS的符号,符号是bind的桥梁。文章中对符号的介绍比较详细,包含之前很少提到的lazy symbol,weak symbol等。
1441+
1442+
1.2 [给实习生讲明白 Lazy/Non-lazy Binding](https://juejin.cn/post/7001842254495268877 "给实习生讲明白 Lazy/Non-lazy Binding") -- 来自掘金:No
1443+
1444+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):这篇文章是对bind讲解的浅显易懂,非常适合之前不了解bind的同学阅读。
1445+
1446+
1.3 [图解 Mach-O 中的 got](https://juejin.cn/post/6918645161303998478 "图解 Mach-O 中的 got") -- 来自掘金:微微笑的蜗牛
1447+
1448+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):这篇文章也是介绍相关知识的,可以补充阅读。
1449+
1450+
2、**关于iOS15的fixup机制**
1451+
1452+
2.1 [iOS 15 如何让你的应用启动更快]( https://juejin.cn/post/6978750428632580110 "iOS 15 如何让你的应用启动更快") -- 来自掘金:ZacJi
1453+
1454+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):iOS15的fixup介绍将主要通过三篇文章,逐次加深深度。阅读这篇文章后,大家应该要弄清楚作者所说的启动加速的原因,以及与二进制重排是否有关系。
1455+
1456+
2.2 [从野指针探测到对iOS 15 bind 的探索](https://mp.weixin.qq.com/s/BNIWBwemmz4isbjBb9-pnQ) -- 来自公众号:皮拉夫大王在此
1457+
1458+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):在阅读了《iOS 15 如何让你的应用启动更快》,进一步探索了bind机制并且加以应用。
1459+
1460+
2.3 [iOS15 动态链接 fixup chain 原理详解](https://mp.weixin.qq.com/s/k_RI2in_Q5hwT33KWig34A) -- 来自公众号:字节跳动终端技术
1461+
1462+
[@皮拉夫大王](https://juejin.cn/user/281104094332653):更加完善地介绍iOS 15的fixup机制。
1463+
1464+
1465+
***
1466+
整理编辑:[@我是熊大](https://github.com/Tliens)
1467+
1468+
> 本期优秀博客的主题为:iOS 内购。
1469+
1470+
1、[iOS内购详解](https://juejin.cn/post/7029252038252822564 "iOS内购详解") -- 来自掘金:QiShare
1471+
1472+
[@我是熊大](https://github.com/Tliens):本文是QiShare针对内购写的一篇文章,包含了内购前的准备、内购流程、恢复购买、内购掉单等内容。
1473+
1474+
2、[iOS内购(IAP)自动续订订阅类型总结](https://www.jianshu.com/p/9531a85ba165 "iOS内购(IAP)自动续订订阅类型总结") -- 来自简书:凡几多
1475+
1476+
[@我是熊大](https://github.com/Tliens):本文主要介绍自动订阅的相关情况。自定订阅与其他的购买不同,是比较复杂的一种情况。自定续期订阅类是有连续性的,其中还有免费试用期、促销期、宽限期的概念。用户还可以取消续订,恢复续订等,这无疑又增加了复杂性。
1477+
1478+
3、[iOS项目技术还债之路《二》IAP掉单优化](https://juejin.cn/post/6844904021229060103 "iOS项目技术还债之路《二》IAP掉单优化") -- 来自掘金:njuxjy
1479+
1480+
[@我是熊大](https://github.com/Tliens):IAP调单一定是大多数开发者不可避免的问题,作者针对调单情况做了非常详细的总结,如果你也正有类似的问题,推荐阅读。
1481+
1482+
4、[苹果iOS内购三步曲:App内退款、历史订单查询、绑定用户防掉单!--- WWDC21](https://juejin.cn/post/6974733392260644895 "苹果iOS内购三步曲:App内退款、历史订单查询、绑定用户防掉单!--- WWDC21") -- 来自掘金:37手游iOS技术运营团队
1483+
1484+
[@我是熊大](https://github.com/Tliens):本文是基于WWDC21的总结,介绍了最新的内购情况,StoreKit 2的出现让内购更简单。惊喜的是:客户端已经支持用户退款了。
1485+
1486+
5、[SwiftyStoreKit](https://github.com/bizz84/SwiftyStoreKit "SwiftyStoreKit") -- 来自SwiftyStoreKit
1487+
1488+
[@我是熊大](https://github.com/Tliens):一个star高达5.9k的开源库,支持内购查询、购买、校验、结束交易等。api简洁易懂,能帮助你在项目中快速接入内购,美中不足的是不支持订单退订,这还需要自己开发。
1489+

‎CategorySummary/Concepts.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1331,4 +1331,4 @@ Flutter 仍有一些缺点,即导致包体变大,iOS 引入后,包体积
13311331
推荐文章:[Flutter实用教程](https://flutter.cn/docs/cookbook "Flutter实用教程")
13321332

13331333

1334-
******************************************************************************************************
1334+
************************************************************************************************************

‎CategorySummary/Interview.md

+194
Original file line numberDiff line numberDiff line change
@@ -2829,3 +2829,197 @@ static unsigned int indexForPointer(const void *p) {
28292829
* [【译】CPU 高速缓存原理和应用](https://segmentfault.com/a/1190000022785358 "【译】CPU 高速缓存原理和应用")
28302830
28312831
2832+
***
2833+
整理编辑:[JY](https://juejin.cn/user/1574156380931144)
2834+
2835+
### 事件响应与传递
2836+
2837+
#### 当指尖触碰屏幕,触摸事件由触屏生成后如何传递到当前应用?
2838+
2839+
通过 `IOKit.framework` 事件发生,被封装为 `IOHIDEvent `对象,然后通过 `mach port` 转发到 `SpringBoard`(也就是桌面)。然后再通过`mach port`转发给当前 APP 的主线程,主线程`Runloop`的`Source1`触发,`Source1`回调内部触发`Source0回调`,`Source0`的回调内部将事件封装成`UIEvent` ,然后调用`UIApplication`的`sendEvent`将`UIEvent`传给了`UIWindow`。
2840+
2841+
> `souce1`回调方法: `__IOHIDEventSystemClientQueueCallback()`
2842+
>
2843+
> `souce0`回调方法: `__UIApplicationHandleEventQueue()`
2844+
2845+
寻找最佳响应者,这个过程也就是`hit-testing`,确定了响应链,接下来就是传递事件。
2846+
2847+
如果事件找不到能够响应的对象,最终会释放掉。`Runloop` 在事件处理完后也会睡眠等待下一次事件。
2848+
2849+
#### 寻找事件的最佳响应者(Hit-Testing)
2850+
2851+
当 APP 接受到触摸事件后,会被放入到当前应用的一个事件队列中(先发生先执行),出队后,`Application` 首先将事件传递给当前应用最后显示的`UIWindow`,询问是否能够响应事件,若窗口能够响应事件,则向下传递子视图是否能响应事件,优先询问后添加的视图的子视图,如果视图没有能够响应的子视图了,则自身就是最合适的响应者。
2852+
2853+
```objectivec
2854+
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
2855+
//3种状态无法响应事件
2856+
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
2857+
//触摸点若不在当前视图上则无法响应事件
2858+
if ([self pointInside:point withEvent:event] == NO) return nil;
2859+
//从后往前遍历子视图数组
2860+
int count = (int)self.subviews.count;
2861+
for (int i = count - 1; i >= 0; i--) {
2862+
// 获取子视图
2863+
UIView *childView = self.subviews[i];
2864+
// 坐标系的转换,把触摸点在当前视图上坐标转换为在子视图上的坐标
2865+
CGPoint childP = [self convertPoint:point toView:childView];
2866+
//询问子视图层级中的最佳响应视图
2867+
UIView *fitView = [childView hitTest:childP withEvent:event];
2868+
if (fitView) {
2869+
//如果子视图中有更合适的就返回
2870+
return fitView;
2871+
}
2872+
}
2873+
//没有在子视图中找到更合适的响应视图,那么自身就是最合适的
2874+
return self;
2875+
}
2876+
```
2877+
2878+
#### 传递事件
2879+
2880+
找到最佳响应者后开始传递事件
2881+
2882+
`UIApplication sendEvent ` =>`UIWindow sendEvent` =>`UIWindow _sendTouchesForEvent` =>`touchesBegin`
2883+
2884+
#### UIApplication 是怎么知道要把事件传给哪个 window 的?window 又是怎么知道哪个视图才是最佳响应者的呢?
2885+
2886+
`hit-testing`过程中将 `Window``view`绑定在 `UIEvent`上的`touch`对象
2887+
2888+
#### 响应者为什么能够处理响应事件,提供了哪些方法?
2889+
2890+
```objectivec
2891+
//手指触碰屏幕,触摸开始
2892+
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
2893+
//手指在屏幕上移动
2894+
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
2895+
//手指离开屏幕,触摸结束
2896+
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
2897+
//触摸结束前,某个系统事件中断了触摸,例如电话呼入
2898+
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
2899+
```
2900+
2901+
#### 触摸事件如何沿着响应链流动?
2902+
2903+
在确定最佳响应者之后,优先给最佳的对象响应,如果最佳对象要将事件传递给其他响应者,这个从底到上的过程叫做响应链。
2904+
2905+
#### 如果有 UIResponder、手势、UIControl 同时存在,是怎么处理的?
2906+
2907+
系统提供的有默认 `action` 操作的 `UIControl`,例如 `UIButton、UISwitch` 等的单击,响应优先级比手势高,而自定义的却比手势识别器要低,然后才是 `UIResponder`
2908+
2909+
`Window` 在将事件传递给 `hit-tested view` 之前,会先将事件传递给相关的手势识别器,并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消 `hit-tested view`对事件的响应;若手势识别器没能识别事件,`hit-tested view` 才完全接手事件的响应权。
2910+
2911+
#### Window怎么知道要把事件传递给哪些手势识别器?
2912+
2913+
`event` 绑定的`touch`对象维护了一个手势数组,在 `hit-testing` 的过程中收集对应的手势识别器, `Window` 先将事件传递给这些手势识别器,再传给 `hit-tested view`。一旦有手势识别器成功识别了手势,`Application` 就会取消`hit-tested view`对事件的响应。
2914+
2915+
#### 手势识别器与UIResponder对于事件响应的联系?
2916+
2917+
* `Window`先将绑定了触摸对象的事件传递给触摸对象上绑定的手势识别器,再发送给触摸对象对应的 `hit-tested view`
2918+
2919+
* 手势识别器识别手势期间,若触摸对象的触摸状态发生变化,事件都是先发送给手势识别器再发送给 `hit-test view`
2920+
2921+
* 手势识别器若成功识别了手势,则通知 `Application` 取消 `hit-tested view` 对于事件的响应,并停止向 `hit-tested view` 发送事件;
2922+
2923+
* 若手势识别器未能识别手势,而此时触摸并未结束,则停止向手势识别器发送事件,仅向 `hit-test view` 发送事件。
2924+
2925+
* 若手势识别器未能识别手势,且此时触摸已经结束,则向 `hit-tested view` 发送 `end` 状态的 `touch`事件以停止对事件的响应。
2926+
2927+
> **cancelsTouchesInView** 若设置成YES,则表示手势识别器在识别手势期间,截断事件,即不会将事件发送给hit-tested view。
2928+
>
2929+
> **delaysTouchesBegan** 若设置成NO,则在手势识别失败时会立即通知Application发送状态为end的touch事件给hit-tested view以调用 `touchesEnded:withEvent:` 结束事件响应。
2930+
2931+
#### 有哪些情况无法响应?
2932+
2933+
* **不允许交互**`userInteractionEnabled = NO`
2934+
2935+
* **隐藏**`hidden = YES `):如果父视图隐藏,那么子视图也会隐藏,隐藏的视图无法接收事件
2936+
2937+
* **透明度**:alpha < 0.01 如果设置一个视图的透明度<0.01,会直接影响子视图的透明度。alpha:0.0~0.01为透明。
2938+
2939+
### 参考
2940+
2941+
[iOS触摸事件全家桶](https://www.jianshu.com/p/c294d1bd963d "iOS触摸事件全家桶")
2942+
2943+
***
2944+
整理编辑:[Hello World](https://juejin.cn/user/2999123453164605/posts)
2945+
2946+
### mmap 应用
2947+
2948+
`mmap`是系统提供的一种虚拟内存映射文件的技术。可以将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程中虚拟内存地址之间的映射关系。
2949+
2950+
在 iOS 中经常用在对性能要求较高的场景使用。例如常见的 `APM` 的日志写入,大文件读写操作等。
2951+
2952+
> `mmap`还有可以用来做共享内存进程通信、匿名内存映射,感兴趣的同学可以自行学习
2953+
2954+
#### 普通`I/O`流程
2955+
2956+
普通的读写操作,由于考虑虚拟内存权限安全的问题,所有操作系统级别的行为(例如 `I/O`)都是在内核态处理的。同时 `I/O` 操作为了平衡主存和磁盘之间的读写速度以及保护磁盘写入次数,做了缓存处理,即 `page cache`该缓存是位于内核态主存中的。
2957+
2958+
内核态空间,用户进程是无法直接访问的,可以间接通过**系统调用**获取并拷贝到用户态空间进行读取。 即一次读操作的简化流程为:
2959+
2960+
1. 用户进程发起读取数据操作`read()`
2961+
2962+
2. `read()`通过系统调用函数调用内核态的函数读取数据
2963+
2964+
3. 内核态会判断读取内存页是否在 `Page Cache`中,如果命中缓存,则直接拷贝到主存中供用户进程使用
2965+
2966+
4. 如果未命中,则先从磁盘将数据按照 `Page Size`对齐拷贝到 `Page Cache`中,然后再次执行上面步骤 3
2967+
2968+
所以一次普通读写,最多需要经历两次数据拷贝,一次是从磁盘映射到 `Page cache`,第二次是`Page Cachef`拷贝到用户进程空间。
2969+
2970+
以上只是简化后的流程,对文件读写操作感兴趣的可以通过该文章学习[从内核文件系统看文件读写过程 ](https://www.cnblogs.com/huxiao-tee/p/4657851.html "从内核文件系统看文件读写过程")
2971+
2972+
#### 优缺点
2973+
2974+
由上可知 `mmap`相比普通的文件读写,优势在于可以有选择的映射,只加载一部分内容到进程虚拟内存中。另一方面,由于 `mmap`是直接映射磁盘文件到虚拟内存,减少了数据交换的次数,所以写入性能也更快。
2975+
2976+
在存在优势的同时,也有一些缺点,例如 `mmap` 要求加载的最小单位为 `VM Page Size`,所以如果是小文件,该方法会导致碎片空间浪费。
2977+
2978+
#### mmap API 示例
2979+
2980+
`mmap` 实际应用主要是 `mmap() & munmap()`两个函数实现。两个函数原型如下:
2981+
2982+
```cpp
2983+
/// 需要导入头文件
2984+
#import <sys/mman.h>
2985+
2986+
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
2987+
int munmap(void* start,size_t length);
2988+
```
2989+
2990+
函数参数:
2991+
2992+
- `start`:映射区的其实位置,设置为零表示由系统决定映射区的起始位置
2993+
- `length`: 映射区长度,单位是字节, 不足一页内存按一整页处理
2994+
- `prot`:期望的内存保护标志,不能与文件打开模式冲突,支持 `|` 取多个值
2995+
- `PROT_EXEC`: 页内容允许执行
2996+
- `PROT_READ`:页内容允许读取
2997+
- `PROT_WRITE`:页内容可以写入
2998+
- `PROT_NONE`:不可访问
2999+
- `flags`:指定映射对象的类型,映射选项和映射页是否可以共享(这里只注释使用的两项,其他更多定义可以自行查看)
3000+
- `MAP_SHARED`:与其它所有映射这个文件对象的进程共享映射空间。对共享区的写入,相当于输出到文件。
3001+
- `MAP_FILE`:默认值,表示从文件中映射
3002+
- `fd`:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明是匿名映射。
3003+
- `off_set`:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应offset必须是分页大小的整数倍。
3004+
3005+
`mmap` 回写时机并不是实时的,调用 `msync()`或者`munmap()` 时会从内存中回写到文件,系统异常退出也会进行内容回写,不会导致日志数据丢失,所以特别适合日志文件写入。
3006+
3007+
> Demo 可以参考开源库 `OOMDetector` 中的 `HighSppedLogger` 类的使用封装,有比较完整的映射、写入、读取、同步的代码封装,可直接使用。
3008+
3009+
#### 注意事项
3010+
3011+
`mmap` 允许映射到内存中的大小大于文件的大小,最后一个内存页不被使用的空间将会清零。但是如果映射的虚拟内存过大,超过了文件实际占用的内存页数量,后续访问会抛出异常。
3012+
3013+
示例可以参考[认真分析mmap:是什么 为什么 怎么用 ](https://www.cnblogs.com/huxiao-tee/p/4660352.html)中的情景二:
3014+
3015+
![](http://cdn.zhangferry.com/Images/weekly_51_interview.png)
3016+
3017+
超出文件大小的虚拟内存区域,文件所在页的内存扔可以访问,超出所在页的访问会抛出 `Signal` 信号异常
3018+
3019+
#### 参考
3020+
3021+
- [认真分析mmap:是什么 为什么 怎么用 ](https://www.cnblogs.com/huxiao-tee/p/4660352.html "认真分析 mmap: 是什么 为什么 怎么用")
3022+
- [C语言mmap()函数:建立内存映射](http://c.biancheng.net/cpp/html/138.html "C语言mmap()函数:建立内存映射")
3023+
- [OOMDetector](https://github.com/Tencent/OOMDetector "OOMDetector")
3024+
3025+

‎CategorySummary/Resources.md

+19
Original file line numberDiff line numberDiff line change
@@ -698,3 +698,22 @@ Joel Spolsky 的 Blog 纸质版文集,中文版由阮一峰翻译。作者 Joe
698698

699699
![](http://cdn.zhangferry.com/Images/20220331222838.png)
700700

701+
***
702+
整理编辑:[Mimosa](https://juejin.cn/user/1433418892590136)
703+
704+
### 闲话 Swift 协程
705+
706+
**地址**https://www.bennyhuo.com/book/swift-coroutines/
707+
708+
该系列博客从浅入深地介绍了 Swift 在 5.5 中新支持的协程特性。该系列文章介绍了 Swift 协程的特性,内容以 Swift 协程的基本概念、语法设计、使用场景等方面为基础展开,也会与大前端开发者常见的 Kotlin、JavaScript 做对比(作者是 Kotlin GDE),作者希望这个系列能给大家一个更多元化的视角来理解这个语法特性,十分推荐。
709+
710+
***
711+
整理编辑:[Mimosa](https://juejin.cn/user/1433418892590136)
712+
713+
### iOS 高性能app架构
714+
715+
**地址**https://github.com/dudongge/iOS_Architecture
716+
717+
该仓库是 [Advanced iOS App Architecture (1st Edition)](https://zh.sg1lib.org/book/5002805/90c154) 的翻译本,对于译文修改了一些错别字,有 pdf 和 word 可以选择。本书主要讨论了在开发 App 的时候,代码在各种架构中的表现和细节的不同,讨论了各种架构的优缺点以及在 iOS 中,这些架构又有何特点和不同。
718+
719+

0 commit comments

Comments
 (0)
Please sign in to comment.