mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
update: image source
This commit is contained in:
@@ -10,7 +10,7 @@ NSTimer、CADisplayLink 的 基础 API `[NSTimer scheduledTimersWithTimeInterval
|
||||
|
||||
栈、堆、BSS、数据段、代码段
|
||||
|
||||

|
||||

|
||||
|
||||
栈(stack):又称作堆栈,用来存储程序的局部变量(但不包括static声明的变量,static修饰的数据存放于数据段中)。除此之外,在函数被调用时,栈用来传递参数和返回值。栈内存地址越来越少
|
||||
|
||||
@@ -37,7 +37,7 @@ BSS段(bss segment):通常用来存储程序中未被初始化的全局变
|
||||
|
||||
代码段(code segment):编译之后的代码。通常是指用来存储程序可执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。
|
||||
|
||||

|
||||

|
||||
|
||||
上 Demo 验证
|
||||
|
||||
@@ -147,7 +147,7 @@ Demo1
|
||||
|
||||
运行该代码会 Crash,报错信息如下
|
||||
|
||||

|
||||

|
||||
|
||||
说明:一开始的报错信息只说坏内存访问,但是并没有显示具体的方法调用堆。想知道具体 Crash 原因还是需要看看堆栈比较方便。输入 bt 查看最后是由于 `objc_release` 方法造成 crash。
|
||||
|
||||
@@ -257,7 +257,7 @@ static inline bool _objc_isTaggedPointer(const void * _Nullable ptr)
|
||||
|
||||
tips:某些对象虽然是 TaggedPointer 类型,但是打印 class 发现不是,猜测可能是系统用类簇隐藏了某些实现细节。比如下面
|
||||
|
||||

|
||||

|
||||
|
||||
针对 NSNumber 的 TaggedPoniter 的 case,查看 class 打印出 `__NSCFNumber`。但根据源码和内存高地址位分析确实是 TaggedPoniter。
|
||||
|
||||
@@ -826,7 +826,7 @@ void sel_init(size_t selrefCount){
|
||||
|
||||
在 gone 处加断点,利用 runtime 查看类中的方法信息
|
||||
|
||||

|
||||

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

|
||||

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

|
||||

|
||||
|
||||
### 深入 .cxx_destruct
|
||||
|
||||
@@ -1123,7 +1123,7 @@ class AutoreleasePoolPage {
|
||||
- 每个 AutoreleasePoolPage 对象占用 4096 字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放 autorelease 对象的地址
|
||||
- 所有的 AutoreleasePoolPage 对象通过**双向链表**的形式连接在一起。child 指向下一个对象,parent 指向上一个对象
|
||||
|
||||

|
||||

|
||||
|
||||
```objectivec
|
||||
id * begin() {
|
||||
@@ -1181,7 +1181,7 @@ int main(int argc, const char * argv[]) {
|
||||
|
||||
main 方法内部3个 autoreleasepool 底层怎么样工作的?
|
||||
|
||||

|
||||

|
||||
|
||||
3个@auto releasepool, 系统遇到第一个的时候底层就是初始化一个结构体 `__AtAutoreleasePool`,结构体构造方法内部调用 `AutoreleasePoolPage::push` 方法,系统给 AutoreleasePoolPage 真正保存 autorelease 对象的地方存储进一个 `POOL_BOUNDARY` 对象,然后储存 P1、P2 对象地址,遇到第二个则继续初始化结构体,调用 push 方法,存储一个` POOL_BOUNDARY` 对象,继续保存 P3,遇到第三个则继续初始化结构体,调用 push 方法,存储一个 `POOL_BOUNDARY` 对象,继续保存 P4。
|
||||
|
||||
@@ -1963,7 +1963,7 @@ static inline id *autoreleaseFast(id obj) {
|
||||
|
||||
每当进行一次`objc_autoreleasePoolPush`调用时,runtime 向当前的 AutoreleasePoolPage 中 add 进一个`哨兵对象`,值为0(也就是个nil),那么这一个page就变成了下面的样子:
|
||||
|
||||

|
||||

|
||||
|
||||
`objc_autoreleasePoolPush`的返回值正是这个哨兵对象的地址,被`objc_autoreleasePoolPop(哨兵对象)`作为入参,于是:
|
||||
|
||||
@@ -1981,7 +1981,7 @@ iOS 在主线程的 Runloop 中注册了2个 Observer
|
||||
|
||||
结合 RunLoop 运行图
|
||||
|
||||

|
||||

|
||||
|
||||
- 01 通知 Observer 进入 Loop 会调用 `objc_autoreleasePoolPush`
|
||||
|
||||
@@ -2070,7 +2070,7 @@ NSHashMap、NSMapTable 都可以描述 key、value 的内存修饰。
|
||||
|
||||
这段代码运行会 crash,信息如下
|
||||
|
||||

|
||||

|
||||
|
||||
原因是 NSError 构造方法内部会加 autorelease。源码如下
|
||||
|
||||
@@ -2133,7 +2133,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