docs: image url

This commit is contained in:
FantasticLBP
2026-01-02 10:28:57 +08:00
parent 7ac7513900
commit 7843661458
29 changed files with 719 additions and 719 deletions

View File

@@ -315,7 +315,7 @@ dispatch_sync(dispatch_get_main_queue(), nil);
Demo1
<img src="./../assets/RunloopPerformSelector.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelector.png" style="zoom:25%" />
QA为什么先打印1、3再打印2
@@ -332,7 +332,7 @@ Demo2:
<img src="./../assets/RunloopPerformSelectorAfterDelay.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelay.png" style="zoom:25%" />
@@ -372,13 +372,13 @@ QA为什么 test 里的2没有打印
所以代码改下就可运行。
<img src="./../assets/RunloopPerformSelectorAfterDelayFix.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelayFix.png" style="zoom:25%" />
注意:可能有一部分人会这么在子线程中添加 RunLoop会存在3无法打印的问题。为什么
<img src="./../assets/RunloopPerformSelectorAfterDelayFix1.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelayFix1.png" style="zoom:25%" />
`[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];` 本质上让 RunLoop 在指定模式下运行,直到发生以下情况之一:
@@ -391,15 +391,15 @@ QA为什么 test 里的2没有打印
第一种:在子线程方法中,手动关闭子线程中的 RunLoop
<img src="./../assets/RunloopPerformSelectorAfterDelayFix2.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelayFix2.png" style="zoom:25%" />
第二种:不要用 `[NSDate distantFuture]`,设置个0秒也可以改为 `[NSDate dateWithTimeIntervalSinceNow:0]]`
<img src="./../assets/RunloopPerformSelectorAfterDelayFix3.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelayFix3.png" style="zoom:25%" />
第三种:不要用 `[NSDate distantFuture]`,设置个0秒也可以改为 `[NSDate dateWithTimeIntervalSinceNow:0]]`。另外不要加 Port直接在子线程中先获取一次 RunLoop 就好,因为 ``performSelector...withObject...afterDelay...` ` 已经给当前的 RunLoop 添加了 NSTimer只是没有开启。 分析 RunLoop 源码分析后会发现,在子线程中获取一次 RunLoop会默认创建一个 RunLoop。
<img src="./../assets/RunloopPerformSelectorAfterDelayFix4.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunloopPerformSelectorAfterDelayFix4.png" style="zoom:25%" />
所以要研究 iOS 底层的同学,看看 **GUNStep 代码吧,这是宝藏**
@@ -409,13 +409,13 @@ QA为什么 test 里的2没有打印
Demo1:
<img src="./../assets/GCDThreadWillTerminateWhenBlockFinished.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/GCDThreadWillTerminateWhenBlockFinished.png" style="zoom:30%" />
同理GCD 虽然开启了子线程,但是 Block 结束后线程也就结束了。所以线程任务中的1秒后的任务肯定也结束了。
Demo2:
<img src="./../assets/NSThreadWillTerminateSoCannotUse.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSThreadWillTerminateSoCannotUse.png" style="zoom:25%" />
可以看到 NSThread 里的 block 执行结束后thread 结束了。后面的 performSelector 想在线程里执行任务,就会 crash。
@@ -528,7 +528,7 @@ static void *nsthreadLauncher(void *thread) {
所以解决办法也是在线程的 block 里面加 RunLoop让它保活
<img src="./../assets/NSThreadWillTerminateSoCanUseViaRunLoopPort.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSThreadWillTerminateSoCanUseViaRunLoopPort.png" style="zoom:30%" />
@@ -569,7 +569,7 @@ dispatch_group_notify(group, dispatch_get_main_queue(), ^{
});
```
<img src="./../assets/DispatchgroupNotify.png" style="zoom:50%"/>
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/DispatchgroupNotify.png" style="zoom:50%"/>
@@ -577,7 +577,7 @@ dispatch_group_notify(group, dispatch_get_main_queue(), ^{
### 经典 Demo
<img src="./../assets/BlockAndQueue.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/BlockAndQueue.png" style="zoom:30%" />
会输出什么?
@@ -802,21 +802,21 @@ sistep instruction简写为 stepisi。当你在 Xcode 汇编面板看
第一步:当第二次调用 saveMoney 方法,开启汇编调试
<img src="./../assets/OSSpinLock-Assemble2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/OSSpinLock-Assemble2.png" style="zoom:30%" />
看到可疑方法 `OSSpinLockLock`给它加断点看到第10行高亮了。lldb 模式输入 c敲回车。次数输入 si 即可进入 `OSSpinLockLock`  方法内部调试
第二步:继续输入 si敲回车
<img src="./../assets/OSSpinLock-Assemble3.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/OSSpinLock-Assemble3.png" style="zoom:30%" />
第三步:看到可疑方法 `_OSSpinLockLockSlow`给它加断点lldb 输入 C。此时断点到这一行了继续输入 si。
<img src="./../assets/OSSpinLock-Assemble4.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/OSSpinLock-Assemble4.png" style="zoom:30%" />
第四步:在 `OSSpinLockLockSlow` 方法内部调试,不断输入 si。
<img src="./../assets/OSSpinLockAssemble1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/OSSpinLockAssemble1.png" style="zoom:30%" />
发现不断 si 最终一直会在第6行到第19行之间执行。懂汇编的会发现这其实是一个 while 循环。便可以证明自旋锁 OSSpinLock 在等锁的时候,底层实现是执行 while 循环,忙等,“太浪费性能了”(如果使用锁资源的线程任务很简单,那自旋也是高效的,可以快速获取锁。)
@@ -915,26 +915,26 @@ int cursorr = 1;
假如对存钱过程,忘记解锁怎么办?产生死锁,如下
<img src="./../assets/Thread-deadlock-unfaillock.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/Thread-deadlock-unfaillock.png" style="zoom:30%" />
添加 cursor 标记死锁是发生在 `saveMoney` 方法执行的第几次。发现是第二次。因为第一次锁没有任何使用方,所以加锁成功,当第二次加锁的时候发现锁没有释放,所以产生死锁。
这时候使用尝试加锁 API `os_unfair_lock_trylock` 即可成功如下
<img src="./../assets/Thread-deadlock-unfairTrylock.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/Thread-deadlock-unfairTrylock.png" style="zoom:30%" />
#### 汇编剖析实现原理
同样方式看看 ,按照上述调试汇编代码的步骤,我将关键步骤截图如下
<img src="./../assets/osunfairlock-assemble1.png" style="zoom:30%" />
<img src="./../assets/osunfairlock-assemble2.png" style="zoom:30%" />
<img src="./../assets/osunfairlock-assemble3.png" style="zoom:30%" />
<img src="./../assets/osunfairlock-assemble4.png" style="zoom:30%" />
<img src="./../assets/osunfairlock-assemble5.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble3.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble4.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble5.png" style="zoom:30%" />
<img src="./../assets/osunfairlock-assemble6.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/osunfairlock-assemble6.png" style="zoom:30%" />
结论:可以看到 `os_unfair_lock` 在锁等待的时候,底层调用的是 `sysCall`,当这一步执行后会发现后续代码都不执行了,也就是调用系统底层能力,线程真正休眠了,而不是一个循环忙等的实现,所以性能好。
@@ -985,7 +985,7 @@ pthread_mutex_destroy(&_moneyLock);
使用如下
<img src="./../assets/PThreadMutextLock.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutextLock.png" style="zoom:25%" />
#### 化身递归锁
@@ -1033,7 +1033,7 @@ pthread_mutex_destroy(&_moneyLock);
改进后的效果如下
<img src="./../assets/PThreadMutextRecursiveLock.png" style="zoom:25%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutextRecursiveLock.png" style="zoom:25%" />
@@ -1045,33 +1045,33 @@ QA互斥递归锁可以在不同线程中加锁吗
#### 汇编剖析实现原理
<img src="./../assets/PThreadMutexLock1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock1.png" style="zoom:30%" />
输入 si 继续跟进可以看到还是在执行我们自己的代码LockExplore image 的 `pthread_mutex_lock` 方法
<img src="./../assets/PThreadMutexLock2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock2.png" style="zoom:30%" />
继续输入 si 跟进
<img src="./../assets/PThreadMutexLock3.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock3.png" style="zoom:30%" />
可以看到此时调用到系统 `libsystem_pthread.dylib` 库的 `pthread_mutex_lock` 方法了。
第41行看到关键函数继续输入 si 进去看看
<img src="./../assets/PThreadMutexLock4.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock4.png" style="zoom:30%" />
可以看到内部第62行关键函数调用了 `_pthread_mutex_firstfit_lock_wait` 方法。此时继续输入 si 跟踪看看
<img src="./../assets/PThreadMutexLock5.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock5.png" style="zoom:30%" />
可以看到内部第25行调用了关键函数 `__psynch_mutexwait`,继续输入 si 看看
<img src="./../assets/PThreadMutexLock6.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock6.png" style="zoom:30%" />
可以看到内部继续调用了系统 `libsystem_pthread.dylib` 库的 `__psynch_mutexwait` 方法。继续输入 si
<img src="./../assets/PThreadMutexLock7.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadMutexLock7.png" style="zoom:30%" />
可以看到内部第4行发生了系统调用 `sysCall`,执行完第四句指令,线程立马就结束了。
@@ -1256,7 +1256,7 @@ CocoaLumberjack 的 DDLog.m 中,锁返回值检查的严谨写法:
激活所有等待该条件的线程 `pthread_cond_broadcast(&_condition)`
<img src="./../assets/PThreadConditionLock.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadConditionLock.png" style="zoom:30%" />
可以看到同时调用 remove、add 方法
@@ -1358,13 +1358,13 @@ NSRecursiveLock 不能在多线程下递归调用。@synchronized 可以在多
Demo
<img src="./../assets/NSLockDemo.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSLockDemo.png" style="zoom:30%" />
NSLock 死锁
<img src="./../assets/NSLockDeadLock.png" style="zoom:40%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSLockDeadLock.png" style="zoom:40%" />
会发生死锁后续代码无法执行App 表现就是 ANR。重复对 NSLock 进行加锁可能导致死锁问题,同时也可能引发数据竞争和性能下降等并发相关隐患
@@ -1414,7 +1414,7 @@ API
Demo
<img src="./../assets/NSConditionLockDemo.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSConditionLockDemo.png" style="zoom:30%" />
@@ -1426,7 +1426,7 @@ Demo
疑问:调用 signal 方法后,另一个等待锁的地方会立马得到锁资源吗?可以做个实验,给 signal 后 sleep 2秒再调用 unlock
<img src="./../assets/NSConditionOrder1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSConditionOrder1.png" style="zoom:30%" />
观察打印信息可以看到:
@@ -1438,7 +1438,7 @@ Demo
<img src="./../assets/NSConditionOrder2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSConditionOrder2.png" style="zoom:30%" />
@@ -1479,7 +1479,7 @@ API 如下:
Demo
<img src="./../assets/NSConditionLockDemo1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSConditionLockDemo1.png" style="zoom:30%" />
分析虽然通过3个线程设置了线程的先后顺序但是多线程任务执行的时候到底谁先执行是没办法控制的。但是通过 `NSConditionLock lockWhenCondition:*` 的能力,可以控制线程的执行顺序。
@@ -1495,7 +1495,7 @@ Demo
线程同步的本质就是多线程的任务是顺序执行
<img src="./../assets/SerialQueueToSolveThreadSyn.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/SerialQueueToSolveThreadSyn.png" style="zoom:30%" />
@@ -1509,11 +1509,11 @@ semaphore 叫做”信号量”
信号量的初始值为1代表同时只允许1条线程访问资源保证线程同步
<img src="./../assets/DispatchSemaphoreControlThreadCount1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/DispatchSemaphoreControlThreadCount1.png" style="zoom:30%" />
可以看到打印了20个线程但是我们控制线程最大数量怎么办呢可以用信号量实现。效果如下
<img src="./../assets/DispatchSemaphoreControlThreadCount2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/DispatchSemaphoreControlThreadCount2.png" style="zoom:30%" />
#### dispatch_semaphore_wait 原理
@@ -1527,7 +1527,7 @@ semaphore 叫做”信号量”
所以如何让线程同步?设置信号量的值=1即可。保证同一时间只有一个线程任务在执行。代码如下
<img src="./../assets/SemaphoreMethodToControlThreadSync.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/SemaphoreMethodToControlThreadSync.png" style="zoom:30%" />
@@ -1587,7 +1587,7 @@ dispatch_semaphore_signal(semaphore);
`@synchronized` 使用很方便,它是对 `pthread_mutex_t` 递归锁的封装。Demo 如下
<img src="./../assets/SynchronizedControlThreadSync.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/SynchronizedControlThreadSync.png" style="zoom:30%" />
@@ -1595,9 +1595,9 @@ dispatch_semaphore_signal(semaphore);
为了探究下实现,开启汇编调试
<img src="./../assets/synchronized-asemble1.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/synchronized-asemble1.png" style="zoom:30%" />
<img src="./../assets/synchronized-asemble2.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/synchronized-asemble2.png" style="zoom:30%" />
通过汇编可以看到 `@synchronized` 底层调用了 `objc_sync_enter` 方法,其中又调用了 `id2data` 和 `os_unfair_recursive_lock_lock_with_options` 方法。 可以查看 objc4 的源码(笔者的 objc 版本为 objc4-objc4-912.3),查找 `objc_sync_enter`
@@ -2106,7 +2106,7 @@ pthread_rwlock_init(&_lock, NULL)
Demo
<img src="./../assets/PThreadRWLockDemo.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PThreadRWLockDemo.png" style="zoom:30%" />
@@ -2134,7 +2134,7 @@ dispatch_barrier_async(self.queue, ^{
上 Demo
<img src="./../assets/DispatchBarrierReadWriteDemo.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/DispatchBarrierReadWriteDemo.png" style="zoom:30%" />
@@ -2163,7 +2163,7 @@ Demo
}
```
<img src="./../assets/GCDBarrierCannotHoldGlobalQueue.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/GCDBarrierCannotHoldGlobalQueue.png" style="zoom:30%" />
@@ -2186,7 +2186,7 @@ Demo
}
```
<img src="./../assets/GCDBarrierCanHoldCustomQueue.png" style="zoom:30%" />
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/GCDBarrierCanHoldCustomQueue.png" style="zoom:30%" />
结论:可以发现 GCD `dispatch_barrier_async` 栅栏函数,拦不住全局队列,却可以拦住自己创建的普通队列。这是为什么?