mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
docs: image url
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
|
||||
假设我们计算有`128MB`内存,程序A需要`10MB`,程序B需要`100MB`,程序C需要`20MB`。如果我们需要同时运行程序A和B,那么比较直接的做法是将内存的`前10MB`分配给程序A,`10MB~110MB`分配给B。
|
||||
|
||||
<img src="./../assets/IOSMemoryWithPhysisMemory.png" style="zoom:20%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/IOSMemoryWithPhysisMemory.png" style="zoom:20%" />
|
||||
|
||||
但存在以下问题:
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
比如A需要`10M`,就假设有`0x00000000` 到`0x00A00000`大小的虚拟空间,然后从物理内存分配一个相同大小的空间,比如是`0x00100000`到`0x00B00000`。操作系统来设置这个映射函数,实际的地址转换由硬件完成。如果越界,硬件就会判断这是一个非法访问,拒绝这个地址请求,并上报操作系统或监控程序。
|
||||
|
||||
<img src="./../assets/iOSMemoryParagraph.png" style="zoom:20%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOSMemoryParagraph.png" style="zoom:20%" />
|
||||
|
||||
这样一来利用**分段**的方式可以解决之前的**地址空间不隔离**和**程序运行地址不确定**
|
||||
|
||||
@@ -78,13 +78,13 @@
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/iOSMemoryPage.png" style="zoom:20%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOSMemoryPage.png" style="zoom:20%" />
|
||||
|
||||
保护页也是页映射的目的之一,简单地说就是每个页可以设置权限属性,谁可以修改,谁可以访问,而且只有操作系统有权修改这些属性,那么操作系统就可以做到保护自己和保护进程。
|
||||
|
||||
虚拟存储的实现需要硬件支持,几乎所有CPU都采用称为**MMU的部件来进行页的映射**
|
||||
|
||||
<img src="./../assets/iOSVirtualMemoryVisitPhysisMemoryViaMMU.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOSVirtualMemoryVisitPhysisMemoryViaMMU.png" style="zoom:40%" />
|
||||
|
||||
在页映射模式下,`CPU`发出的是`Virtual Address`,即我们程序看到的是`虚拟地址`。经过`MMU`转换以后就变成了`Physical Address`。一般`MMU`集成在`CPU`内部,不会以独立的部件存在。
|
||||
|
||||
@@ -102,7 +102,7 @@ NSTimer、CADisplayLink 的 基础 API `[NSTimer scheduledTimersWithTimeInterval
|
||||
|
||||
栈、堆、BSS、数据段、代码段
|
||||
|
||||
<img src="./../assets/iOS-MemoryLayout.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOS-MemoryLayout.png" style="zoom:50%" />
|
||||
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ BSS段(bss segment):通常用来存储程序中未被初始化的全局变
|
||||
|
||||
代码段(code segment):编译之后的代码。通常是指用来存储程序可执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。
|
||||
|
||||

|
||||

|
||||
|
||||
上 Demo 验证
|
||||
|
||||
@@ -210,7 +210,7 @@ Tagged Pointer 格式下,指针值不再是有效抵制,而是表示值。
|
||||
|
||||
当对 TaggedPointer 数据调用方法的时候,objc_msgSend 能识别出如果是 Tagged Pointer,比如 NSNumber 的 intValue 方法,直接从指针提取数据,节省了调用开销。所以使用了 TaggedPointer 技术不仅节约内存空间,又能提高方法查找速度。
|
||||
|
||||
<img src="./../assets/TaggedPointerStructure.png" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerStructure.png" />
|
||||
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ Tagged Pointer 也就是一个伪指针,对象的指针中存储的数据变
|
||||
|
||||
Demo
|
||||
|
||||
<img src="./../assets/TaggedPointerSaveMemoryUsage.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerSaveMemoryUsage.png" style="zoom:30%" />
|
||||
|
||||
在 64 位的 cpu 下,如果使用 Tagged Pointer 技术的话,则 NSNumber 对象的值直接存储在了指针中,系统不会为其在堆上分配内存,可以节省很多内存开销。此时,NSNumber 对象的指针中存储的数据变成了 Tag + Data 的形式(Tag 为特殊标记,用于区分NSNumber、NSDate、NSString 等小内存对象的类型;Data 为具体的值)。这样使用一个 NSNumber 对象只需要在栈中开辟 8 个字节的指针内存。当栈中 8 个字节的指针内存不够存储数据时,才会再将 NSNumber 对象存储到堆中
|
||||
|
||||
@@ -331,7 +331,7 @@ Demo
|
||||
|
||||
路径:Xcode - Edit Scheme - Run - Arguments - Environment Variables - 添加环境变量 `OBJC_DISABLE_TAG_OBFUSCATION` 设置为 YES 即可。
|
||||
|
||||
<img src="./../assets/XcodeDisableTaggedPointerConfuse.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/XcodeDisableTaggedPointerConfuse.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ _objc_decodeTaggedPointer_noPermute_withObfuscator(const void * _Nullable ptr, u
|
||||
}
|
||||
```
|
||||
|
||||
<img src="./../assets/TaggedPointerDecode.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerDecode.png" style="zoom:40%" />
|
||||
|
||||
|
||||
|
||||
@@ -508,7 +508,7 @@ _objc_decodeTaggedPointer_noPermute_withObfuscator(const void * _Nullable ptr, u
|
||||
|
||||
#### Tagged Pointer 与 isa
|
||||
|
||||
<img src="./../assets/TaggedPointerNSNumberDemo.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerNSNumberDemo.png" style="zoom:40%" />
|
||||
|
||||
通过参考 objc 源码,针对对象指针进行解密后发现:
|
||||
|
||||
@@ -539,7 +539,7 @@ b 也就是11,二进制为 `1011`,Tagged Pointer 中,iOS 侧第一位是 T
|
||||
|
||||
3 区分数据类型。具体是什么数据类型,继续做个实验看看
|
||||
|
||||
<img src="./../assets/TaggedPointerDataTypeDemo.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerDataTypeDemo.png" style="zoom:40%" />
|
||||
|
||||
|
||||
|
||||
@@ -560,7 +560,7 @@ b 也就是11,二进制为 `1011`,Tagged Pointer 中,iOS 侧第一位是 T
|
||||
|
||||
Objc 源码中,NSInteger、NSUInteger 都是别名。初始化 NSNumber 的时候用的是 `NSNumber numberWithInteger:<#(NSInteger)#>`
|
||||
|
||||
<img src="./../assets/ObjcNSIntegerIsLong.png" style="zoom:30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/ObjcNSIntegerIsLong.png" style="zoom:30%" >
|
||||
|
||||
|
||||
|
||||
@@ -638,7 +638,7 @@ Tagged Pointer 如何区分是较小的对象,比如 NSString、NSDate、NSNum
|
||||
|
||||
验证下
|
||||
|
||||
<img src="./../assets/TaggedPointerKind.png" style="zoom:30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerKind.png" style="zoom:30%" >
|
||||
|
||||
可以看到:
|
||||
|
||||
@@ -651,11 +651,11 @@ Tagged Pointer 如何区分是较小的对象,比如 NSString、NSDate、NSNum
|
||||
|
||||
下面是针对 double 包装成 NSNumber 的 Tagged Pointer 指针结构拆分:
|
||||
|
||||
<img src="./../assets/NSNumberTaggedPointerStructure.png" style="zoom:90%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSNumberTaggedPointerStructure.png" style="zoom:90%" />
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/NSNumberAddressToBinary.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSNumberAddressToBinary.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -861,7 +861,7 @@ Demo1
|
||||
|
||||
运行该代码会 Crash,报错信息如下
|
||||
|
||||
<img src="./../assets/TaggedPointerCrash.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerCrash.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -884,11 +884,11 @@ Demo1
|
||||
|
||||
改法1:将 property 改为 **atomic** 修饰的。
|
||||
|
||||
<img src="./../assets/TaggedPointerCrashFix1.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerCrashFix1.png" style="zoom:50%" />
|
||||
|
||||
改法2:对 name 加锁
|
||||
|
||||
<img src="./../assets/TaggedPointerCrashFix2.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerCrashFix2.png" style="zoom:50%" />
|
||||
|
||||
|
||||
|
||||
@@ -896,7 +896,7 @@ Demo1
|
||||
|
||||
Demo2
|
||||
|
||||
<img src="./../assets/TaggedPointerWillNotCrash.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/TaggedPointerWillNotCrash.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -952,7 +952,7 @@ NSString、NSMutableString 继承关系如下:
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/NSStringClassClusterAndTaggedPointer.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSStringClassClusterAndTaggedPointer.png" style="zoom:30%" />
|
||||
|
||||
通过 `[[NSString alloc] initWithString:@"**"]` 方式创建的 NSString 字符串
|
||||
|
||||
@@ -1022,27 +1022,27 @@ iOS 中使用引用计数来管理 OC 对象的内存。一个新创建的 OC
|
||||
|
||||
调用 retain/copy 会让 OC 对象的引用计数 +1,调用 release 会让 OC 对象的引用计数 -1。
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue.png" style="30%" >
|
||||
|
||||
可以看到,如果我们提前将 cat 释放了,那后续赋值给 person 的 _cat 成员变量就没法使用了,因为已经释放了,否则就会造成 `EXC_BAD_ACCESS`。这样子太不灵活了。需要改进下:
|
||||
|
||||
调用 setCat 的时候,对传入的 cat 进行 retain,引用计数 +1,谁用谁管理,同样的最后在 Person 对象释放的时候对 cat 进行 release,引用计数 -1.
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue1.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue1.png" style="30%" >
|
||||
|
||||
但上面的代码不完美,还是存在问题。假设 cat1、cat2 2个对象,当作参数调用2次 setCat 方法,如果 setCat 方法内部不做处理,会导致第2次调用 setCat 后,之前调用时传入的 cat1 会无法释放。
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue2.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue2.png" style="30%" >
|
||||
|
||||
修改下。调用 setCat 方法时,对之前的 _cat 调用 release,对旧的引用计数-1,再对新传入的对象调用 retain,让引用计数+1,然后赋值
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue3.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue3.png" style="30%" >
|
||||
|
||||
|
||||
|
||||
上面的代码还是存在问题,会造成僵尸对象问题
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue4.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue4.png" style="30%" >
|
||||
|
||||
分析下 cat 的引用计数情况:
|
||||
|
||||
@@ -1054,7 +1054,7 @@ iOS 中使用引用计数来管理 OC 对象的内存。一个新创建的 OC
|
||||
|
||||
改进
|
||||
|
||||
<img src="./../assets/PersonAndCatMRCIssue5.png" style="30%" >
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/PersonAndCatMRCIssue5.png" style="30%" >
|
||||
|
||||
|
||||
|
||||
@@ -1217,7 +1217,7 @@ NSLog(@"array2 --- %zd", array2.retainCount);
|
||||
|
||||
Demo3
|
||||
|
||||
<img src="./../assets/NSMutableArrayCopyIssue.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSMutableArrayCopyIssue.png" style="zoom:30%" />
|
||||
|
||||
会发现发生了 crash。问题是因为
|
||||
|
||||
@@ -1605,7 +1605,7 @@ class StripedMap {
|
||||
|
||||
### 引用计数表
|
||||
|
||||
<img src="./../assets/ReferenceCountintStructure.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/ReferenceCountintStructure.png" style="zoom:40%" />
|
||||
|
||||
|
||||
|
||||
@@ -1613,7 +1613,7 @@ class StripedMap {
|
||||
|
||||
weak_table_t 结构如下:
|
||||
|
||||
<img src="./../assets/WeakReferenceCountTable.png" style="zoom:70%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/WeakReferenceCountTable.png" style="zoom:70%" />
|
||||
|
||||
```c++
|
||||
#define WEAK_INLINE_COUNT 4
|
||||
@@ -1708,7 +1708,7 @@ objc_destoryWeak(&obj);
|
||||
|
||||
上 Demo
|
||||
|
||||
<img src="./../assets/ObjcStoreWeakWhenUseWeak.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/ObjcStoreWeakWhenUseWeak.png" style="zoom:30%" />
|
||||
|
||||
可以看到当一个 weak 指针被赋值的时候,底层调用了 `objc_initWeak`,跟踪查看 objc 源码
|
||||
|
||||
@@ -2549,7 +2549,7 @@ void sel_init(size_t selrefCount){
|
||||
|
||||
在 gone 处加断点,利用 runtime 查看类中的方法信息
|
||||
|
||||

|
||||

|
||||
|
||||
发现存在 `.cxx_destruct` 方法。
|
||||
|
||||
@@ -2584,7 +2584,7 @@ void sel_init(size_t selrefCount){
|
||||
@end
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
Tips:@property 会自动生成成员变量,另外类后面加 `{}` 在内部也可以加成员变量,假如成员变量是对象类型,比如 NSString,则叫实例变量。
|
||||
|
||||
@@ -2598,7 +2598,7 @@ Tips:@property 会自动生成成员变量,另外类后面加 `{}` 在内部
|
||||
|
||||
在 gone 的地方加断点,输入 `watchpoint set variable p->_name`,则会将 `_name` 实例变量加入 watchpoint,当变量被修改时会触发断点,可以看出从某个值变为 0x0,也就是 nil。此时边上调用堆栈显示在 `objc_storestrong` 方法中,被设置为 nil.
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -2843,7 +2843,7 @@ Person *person2 = [Person personWithName:@"FantasticLBP"];
|
||||
|
||||
隐式调用工厂方法
|
||||
|
||||
<img src="./../assets/ARCWillCrashWhenCallNewMethodDirectly.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/ARCWillCrashWhenCallNewMethodDirectly.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -2853,7 +2853,7 @@ Person *person2 = [Person personWithName:@"FantasticLBP"];
|
||||
|
||||
如何修改?加一个 bridge 即可。
|
||||
|
||||
<img src="./../assets/FixARCWillCrashWhenCallNewMethodDirectly.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/FixARCWillCrashWhenCallNewMethodDirectly.png" style="zoom:30%" />
|
||||
|
||||
由于 ARC 没有加 retain。所以 `person = (__bridge id)result;` 这里完成了对象的 retain。ARC 在退出方法的作用域时给对象加上release。前后对应,内存正确。
|
||||
|
||||
@@ -2964,7 +2964,7 @@ class AutoreleasePoolPage {
|
||||
- 每个 AutoreleasePoolPage 对象占用 4096 (16的3次方,0x2000)字节内存,除了用来存放它内部的成员变量(内部成员固定有7个,56个字节,即 `0x18`, `0x1000 + 0x38 = 0x1038` ),剩下的空间用来存放 autorelease 对象的地址
|
||||
- 所有的 AutoreleasePoolPage 对象通过**双向链表**的形式连接在一起。child 指向下一个 AutoreleasePoolPage 对象,parent 指向上一个 AutoreleasePoolPage 对象
|
||||
|
||||

|
||||

|
||||
|
||||
```objectivec
|
||||
id * begin() {
|
||||
@@ -3288,7 +3288,7 @@ class AutoreleasePoolPage : private AutoreleasePoolPageData {
|
||||
|
||||
举个例子,for 循环创建1000个 Person 对象,用 autorelease 修饰,如何工作?
|
||||
|
||||
<img src="./../assets/AutoreleasePoolPageWithForIterator.png" style="zoom:80%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/AutoreleasePoolPageWithForIterator.png" style="zoom:80%" />
|
||||
|
||||
分析:
|
||||
|
||||
@@ -3327,7 +3327,7 @@ int main(int argc, const char * argv[]) {
|
||||
|
||||
main 方法内部3个 autoreleasepool 底层怎么样工作的?
|
||||
|
||||
<img src="./../assets/AutoreleasePoolMoreItem.png" style="zoom:60%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/AutoreleasePoolMoreItem.png" style="zoom:60%" />
|
||||
|
||||
分析:
|
||||
|
||||
@@ -4226,7 +4226,7 @@ iOS 在主线程的 Runloop **通用模式(Common Modes)** 中注册 **1 个
|
||||
|
||||
结合 RunLoop 运行图
|
||||
|
||||

|
||||

|
||||
|
||||
- 01 通知 Observer 进入 Loop 会调用 `objc_autoreleasePoolPush`
|
||||
|
||||
@@ -4373,7 +4373,7 @@ Cocoa 框架中,很多类方法用于返回 autorelease 对象。
|
||||
### NSTimer、CSDisplayLink 中的内存泄露
|
||||
#### CADisplayLink 内存泄漏
|
||||
|
||||
<img src="./../assets/CSDisplayLinkMemoryLeak.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/CSDisplayLinkMemoryLeak.png" style="zoom:30%" />
|
||||
|
||||
可以看到 CADisplayLink 和 VC,VC 和 CADisplayLink 互相持有,造成内存泄漏,没有释放。即使页面离开,定时器还在继续运行,不断打印。
|
||||
|
||||
@@ -4387,11 +4387,11 @@ NSTimer 的基础 API `[NSTimer scheduledTimersWithTimeInterval:1 repeat:YES blo
|
||||
|
||||
Demo 如下:
|
||||
|
||||
<img src="./../assets/NSTimerMemoeryLeakDemo.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSTimerMemoeryLeakDemo.png" style="zoom:30%" />
|
||||
|
||||
但是当使用 `[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerTask) userInfo:nil repeats:NO];` repeats 为 NO 的时候,好像不会内存泄漏。这是为什么?
|
||||
|
||||
<img src="./../assets/NSTimerMemoeryNotLeakWhenRepeatNO.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSTimerMemoeryNotLeakWhenRepeatNO.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -4563,7 +4563,7 @@ Demo 如下:
|
||||
|
||||
##### 改用 block 的方式替换 API,不再持有 target
|
||||
|
||||
<img src="./../assets/NSTimerFixMemoryLeakIssueByBlockAPI.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSTimerFixMemoryLeakIssueByBlockAPI.png" style="zoom:30%" />
|
||||
|
||||
该种方式,控制器 (self)强引用 timer,timer 强引用 block,block 弱引用 self,3者没有形成环。
|
||||
|
||||
@@ -4623,7 +4623,7 @@ TimerTarget.target(weak) -> VC
|
||||
}
|
||||
```
|
||||
|
||||
<img src="./../assets/NSTimerMemoryLeakFixedByProxy.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSTimerMemoryLeakFixedByProxy.png" style="zoom:30%" />
|
||||
|
||||
解决方案2:使用专门处理消息转发的 NSProxy 类
|
||||
|
||||
@@ -4631,7 +4631,7 @@ TimerTarget.target(weak) -> VC
|
||||
|
||||
##### NSProxy 闪亮登场
|
||||
|
||||
<img src="./../assets/NSTimerMemoryLeakFixedByNSProxy.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSTimerMemoryLeakFixedByNSProxy.png" style="zoom:30%" />
|
||||
|
||||
可以看到使用 NSProxy 也可以解决 NSTimer 和 VC 循环引用的问题。但注意:继承自 NSProxy 的类,不能 init。
|
||||
|
||||
@@ -4645,7 +4645,7 @@ QA:自己写的继承自 NSObject 的代理对象和继承自 NSProxy 的代
|
||||
|
||||
看一段神奇的代码
|
||||
|
||||
<img src="./../assets/NSProxyAndNSObjectMethodImpl.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSProxyAndNSObjectMethodImpl.png" style="zoom:30%" />
|
||||
|
||||
为什么打印出 `0 1`?
|
||||
|
||||
@@ -4684,7 +4684,7 @@ QA:自己写的继承自 NSObject 的代理对象和继承自 NSProxy 的代
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/RunLoop-SourceCode.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RunLoop-SourceCode.png" style="zoom:30%" />
|
||||
|
||||
假设一个 NSTimer 被加到 RunLoop 开头,NSTimer 执行周期为1s,RunLoop 前面任务繁重,第一次走完一个完整的 RunLoop 需要0.4s,然后从头检测 NSTimer 有没有到时间,发现还没到继续执行 RunLoop 后续逻辑。后面遇到卡顿任务了,第二次 RunLoop 用了0.5s,然后从头检测 NSTimer 有没有到时间,0.4+0.5还不到时间,继续跑,第三次 RunLoop 比较轻松,耗时0.2s,再判断定时器时间有没有到,则此次已经0.4+0.5+0.2=1.1s了,此时 NSTimer 的事件被执行,此时精确度已经不够了(每次 RunLoop 的执行时间不固定)
|
||||
|
||||
@@ -4854,7 +4854,7 @@ dispatch_semaphore_t semaphore_;
|
||||
|
||||
说明:直接 `performSelector` 存在警告,可以告诉编译器忽略警告。可以在 Xcode 点开警告,查看详情,复制 `[]` 里面的字符串去忽略警告
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -5035,11 +5035,11 @@ NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
|
||||
|
||||
再来2个实验:
|
||||
|
||||
<img src="./../assets/NSDictionaryCopyTheKey.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSDictionaryCopyTheKey.png" style="zoom:30%" />
|
||||
|
||||
分析:可以看到 p1 的地址,在刚初始化后,和当作 key 加入到 NSDictionary 后,地址发生了变化。对 p1 执行了 copy 操作。
|
||||
|
||||
<img src="./../assets/NSMapTableNSDictionaryMemoryControl.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSMapTableNSDictionaryMemoryControl.png" style="zoom:30%" />
|
||||
|
||||
分析:
|
||||
|
||||
@@ -5090,7 +5090,7 @@ NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
|
||||
|
||||
这段代码运行会 crash,信息如下
|
||||
|
||||

|
||||

|
||||
|
||||
原因是 NSError 构造方法内部会加 autorelease。源码如下
|
||||
|
||||
@@ -5153,7 +5153,7 @@ MRC 下的 `[(id)(object) autorelease]` 等价于 ARC 下的 `id __autoreleasing
|
||||
|
||||
我写了个僵尸对象检测工具,效果如下
|
||||
|
||||

|
||||

|
||||
|
||||
可以定位僵尸对象,并且打印出具体堆栈,并模拟系统行为调用 `abort` 。对监控原理和工具实现感兴趣的可以查看这里[带你打造一套 APM 监控系统-内存监控之野指针/内存泄漏监控](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.74.md#zombieSniffer)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user