本文从 Tagged Pointer、objc 源码、dealloc 原理、AutoreleasePool 原理、野指针探求等技能点打开聊了聊 iOS 内存相关问题。

定时器内存走漏

NSTimer、CADisplayLink 的 根底 API [NSTimer scheduledTimersWithTimeInterval:1 repeat:YES block:nil] 和当时的 VC 都会相互持有,形成环,会存在内存走漏问题。

定时器内存走漏原因,解决方案以及高精度定时器,详细能够看这篇 NSTimer 中的内存泄露 。

iOS 内存布局

栈、堆、BSS、数据段、代码段

剖析 iOS 内存原理

栈(stack):又称作仓库,用来存储程序的局部变量(但不包含static声明的变量,static润饰的数据寄存于数据段中)。除此之外,在函数被调用时,栈用来传递参数和回来值。栈内存地址越来越少

func a {
    变量 1 地址最大
变量 2地址第二大
// ...
变量n地址最小
}

堆(heap):用于存储程序运转中被动态分配的内存段,它的大小并不固定,可动态的扩张和缩减。操作函数(malloc/free)。分配的内存空间地址越来越大。

BSS段(bss segment):一般用来存储程序中未被初始化的全局变量和静态变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段输入静态内存分配

数据段(data segment):一般用来存储程序中已被初始化的全局变量和静态变量和字符串的一块内存区域。数据段包含3部分:

  • 字符串常量。比方 NSString *str = @"杭城小刘";

  • 已初始化数据:现已初始化的全局变量、静态变量等

  • 未初始化数据:未初始化的全局变量、静态变量等

代码段(code segment):编译之后的代码。一般是指用来存储程序可履行代码的一块内存区域。这部分区域的大小在程序运转前就现已确认,并且内存区域一般归于只读,某些架构也答应代码段为可写,即答应修正程序。

剖析 iOS 内存原理

上 Demo 验证

int a = 10;
static int b;
int main () {
    NSString *name = @"杭城小刘";
    int age = 27;
    int height = 177;
    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"\na: %p\nb: %p\n name: %p\nage: %p\n height: %p\nobj:%p", &a, &b, &name, &age, &height, obj);
}
a: 0x107b09b80
b: 0x107b09c48
name: 0x7ff7b83fdbc0
age: 0x7ff7b83fdbbc
height: 0x7ff7b83fdbb8
obj:0x6000012780e0

咱们依照内存地址由低到高排个序(如下),发现和咱们总结的规律一起。

// 字符串常量
name:   0x7ff7b83fdbc0
// 已初始化的全局变量、静态变量
a:      0x107b09b80
// 未初始化的全局变量、静态变量
b:      0x107b09c48
// 堆
obj:    0x6000012780e0
// 栈
height: 0x7ff7b83fdbb8
age:    0x7ff7b83fdbbc
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%p %p %@", obj, &obj, obj);

别离打印 obj指针指向的堆上的内存地址、obj 指针在栈上的地址、obj 内容

Tagged Pointer

先来一个 Demo 敞开本部分内容(画外音:代码很短,但让我发生了一个大大的问号)

- (bool)isTaggedPointer:(const void *)ptr
{
    return ((uintptr_t)ptr & (1UL<<63)) == (1UL<<63);
}
NSNumber *number = [NSNumber numberWithInt:10]; // 0xb0000000000000a2 b:12  1100
NSLog(@"%p %d %@", number, [self isTaggedPointer:(__bridge const void *)number], number.class);
NSString *name1 = [NSString stringWithFormat:@"ss"]; // 0xa000000000073732 a:11 1011
NSLog(@"%p %d %@", name1, [self isTaggedPointer:(__bridge const void *)name1], name1.class);

条件阐明:

创立一个 NSNumer 类型的变量 number,NSString 类型的 name1,代码打印地址、类型。发生一个问题:为什么 NSNumber 是 TaggedPointer,可是 class 却显现 __NSCFNumber ?

static inline bool _objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
  • 经过 objc4 源码研讨写了个判别目标是否是 Tagged Pointer 类型的办法。经过体系源码参阅写了判别办法 isTaggedPointer。调用办法得到 number 目标是 Tagged Pointer 类型

  • 依据 iOS 渠道特性,依据内存地址高位剖析确实是 TaggedPointer 类型 ​

  • 相同的 NSString 指针指向的字符串内容比较少,占用内存没必要开立异的内存时,name1 便是 NSTaggedPointerString,打印出 class 也是 NSTaggedPointerString。调用 isTaggedPointer 得到也是 Tageed Pointer 类型

带着问题开端吧

什么是 Tagged Pointer

iOS 从 64bit 开端引入了Tagged Pointer 技能,用于优化 NSNumber、NSDate、NSString等小目标的存储。

在此之前,创立目标需求动态分配内存、保护引证计数等,目标指针存储的是堆中目标的地址值创立一个目标的流程。先在堆上请求一块内存,然后再在栈上添加一个指针类型,指针指向堆上这块内存。假设是 NSNumber *value = [NSNumber numberWithInt:2] value 是指针长度为8字节,堆上内存16字节。加起来24字节就存一个int 2。

此外还需求保护引证技能,沿用一个实在目标那一套,太大材小用了。

Tagged Pointer 格局,目标指针里边存储的数据变成了:Tag + Data,将数据直接存储在了指针中。当指针不够存储数据时,才会运用动态分配内存的办法来存储数据

objc_msgSend 能辨认 Tagged Pointer,比方 NSNumber 的 intValue 办法,直接从指针提取数据,节省了调用开销。

经典问题

Demo1

- (void)test {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (NSInteger i = 0; i<1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"和洽多好多好多好多事看看上课上课上课"];
        });
    }
}

运转该代码会 Crash,报错信息如下

剖析 iOS 内存原理

阐明:一开端的报错信息只说坏内存拜访,可是并没有显现详细的办法调用堆。想知道详细 Crash 原因仍是需求看看仓库比较便利。输入 bt 检查最终是由于 objc_release 办法形成 crash。

小窍门:运用 LLDB 形式下输入 bt,能够检查仓库。也便是 backtrace 的缩写。

不仔细想或许发现不了问题,看到 objc_release 就会想到是在多线程情况下 NSString 的 setter 办法内,ARC 代码经过编译器最终会依照 MRC 去运转。所以 Setter 相似下面代码。

-(void)setName:(NSString *)name
{
    if (_name!=name) {
        [name retain];
        [_name release];
        _name = name;
    }
}

Demo

- (void)test {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (NSInteger i = 0; i<1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"ss"];
            if (i == 100) {
                NSLog(@"%p %@", self.name, self.name.class);
            }
        });
    }
} 
// 0xa000000000073732 NSTaggedPointerString

相同的代码字符串变短居然不 crash 了?由于命中 Tagged Pointer 逻辑了,检查类型是 NSTaggedPointerString

本问题实质是

  • ARC 代码在编译后实在运转阶段是走 MRC 的,strong、copy 内部都会 release 旧的,copy/retain 新的

  • 多线程情况下拜访 setter 需求加锁

  • 字符串在 NSTaggedPointerString 情况下不存在像 OC 目标的 setter 办法内的 release、copy 操作

怎样判别一个指针是否为Tagged Pointer

检查 objc4 源码

#if TARGET_OS_OSX && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif
#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#   define _OBJC_TAG_INDEX_SHIFT 60
#   define _OBJC_TAG_SLOT_SHIFT 60
#   define _OBJC_TAG_PAYLOAD_LSHIFT 4
#   define _OBJC_TAG_PAYLOAD_RSHIFT 4
#   define _OBJC_TAG_EXT_MASK (0xfUL<<60)
#   define _OBJC_TAG_EXT_INDEX_SHIFT 52
#   define _OBJC_TAG_EXT_SLOT_SHIFT 52
#   define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#else
#   define _OBJC_TAG_MASK 1UL
#   define _OBJC_TAG_INDEX_SHIFT 1
#   define _OBJC_TAG_SLOT_SHIFT 0
#   define _OBJC_TAG_PAYLOAD_LSHIFT 0
#   define _OBJC_TAG_PAYLOAD_RSHIFT 4
#   define _OBJC_TAG_EXT_MASK 0xfUL
#   define _O  BJC_TAG_EXT_INDEX_SHIFT 4
#   define _OBJC_TAG_EXT_SLOT_SHIFT 4
#   define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#endif
static inline bool _objc_isTaggedPointer(const void * _Nullable ptr) 
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

能够看到源码经过 _objc_isTaggedPointer 办法判别是否是 Tagged Pointer 类型。传入目标地址,内部经过 _OBJC_TAG_MASK 按位与运算。

其间 _OBJC_TAG_MASK 是一个宏,宏定义外部有个 if 判读,判别是 Mac OS 并且是 x86_64 架构则为0,否则为1。也便是 Mac OS 并且是 x86_64 架构情况下则与 1UL 按位与,否则与 1UL<<63 按位与。

  • iOS渠道 | Mac 非 x86 渠道: 最高有用位是1(第64bit)1UL<<63

  • Mac 且 x86渠道: 最低有用位是11UL

比方 iOS 渠道下

0xb0000000000000a2 b:12  1100
  1100
& 1000
-------
  1000

tips:某些目标尽管是 TaggedPointer 类型,可是打印 class 发现不是,猜想或许是体系用类簇躲藏了某些完成细节。比方下面

剖析 iOS 内存原理

针对 NSNumber 的 TaggedPoniter 的 case,检查 class 打印出 __NSCFNumber。但依据源码和内存高地址位剖析确实是 TaggedPoniter。

疑问是:为什么 NSNumber 的 TaggedPpinter case 下打印 class 是 __NSCFNumber。假如是类簇躲藏细节完成,为什么相同 KVO 也改变了 isa,可是命名是一个新的姓名,而不是类簇的完成?

和朋友评论后有2种观念(观念不是独立的,而是并且一起建立的。对错难以判定,仅供参阅):

  • 类簇,为了躲藏细节完成

  • KVO 和当时 case 不一起。类簇是体系类的设计,KVO 是针对开发者写的目标所以没有类簇,只能动态生成类,改变原类的 isa,命名为 NSKVONotifying_*** 这样的规则。

类簇

类簇(Class Cluster )是笼统工厂形式在 OC 数组中的完成,NSArray、NSNumber、NSString、NSDictionary 都有表现。借口简略性和拓展性的权衡表现。体系躲藏了较多完成细节,只暴露出简略接口。

- (void)classCus
{
    id obj1 = [NSArray alloc]; // __NSPlaceholderArray
    id obj2 = [NSMutableArray alloc]; // __NSPlaceholderArray
    id obj3 = [obj1 init]; // __NSArray0
    id obj4 = [obj2 init]; // __NSArrayM
    NSLog(@"%@ %@ %@ %@", obj1, obj2, obj3, obj4);
}

调用 alloc 之后发生的是 __NSPlaceholderArray 不符合预期。持续调用 init 发现满意希望了。所以猜想 __NSPlaceholderArray 是一个中心目标,后续的 init 办法便是给中心目标发音讯,再由它做工厂,生成真的目标,这里的 __NSArray0__NSArrayM 对应 NSArray、NSMutableArray

Foundation用了静态实例地址办法来完成,伪代码如下:

static __NSPlacehodlerArray *GetPlaceholderForNSArray() {
    static __NSPlacehodlerArray *instanceForNSArray;
    if (!instanceForNSArray) {
        instanceForNSArray = [[__NSPlacehodlerArray alloc] init];
    }
    return instanceForNSArray;
}
static __NSPlacehodlerArray *GetPlaceholderForNSMutableArray() {
    static __NSPlacehodlerArray *instanceForNSMutableArray;
    if (!instanceForNSMutableArray) {
        instanceForNSMutableArray = [[__NSPlacehodlerArray alloc] init];
    }
    return instanceForNSMutableArray;
}
// NSArray完成
+ (id)alloc {
    if (self == [NSArray class]) {
        return GetPlaceholderForNSArray()
    }
}
// NSMutableArray完成
+ (id)alloc {
    if (self == [NSMutableArray class]) {
        return GetPlaceholderForNSMutableArray()
    }
}
// __NSPlacehodlerArray完成
- (id)init {
    if (self == GetPlaceholderForNSArray()) {
        self = [[__NSArrayI alloc] init];
    }
    else if (self == GetPlaceholderForNSMutableArray()) {
        self = [[__NSArrayM alloc] init];
    }
    return self;
}

别的 iOS Foundation 对静态不可变空目标(当时 case 为数组)做了优化

NSArray *a1 = [[NSArray alloc] init];
NSArray *a2 = [[NSArray alloc] init];
NSArray *a3 = [[NSArray alloc] init];
(lldb) p a1
(__NSArray0 *) $0 = 0x0000000109f50a10 @"0 elements"
(lldb) p a2
(__NSArray0 *) $1 = 0x0000000109f50a10 @"0 elements"
(lldb) p a3
(__NSArray0 *) $2 = 0x0000000109f50a10 @"0 elements"
(lldb) 

若干个不可变的空数组间没有任何特异性,回来一个静态目标。

OC 目标内存办理

iOS 中运用引证计数来办理 OC 目标的内存。一个新创立的 OC 目标引证计数默许是1,当引证计数减为 0,OC 目标就会毁掉,开释其占用的内存空间

调用 retain/copy 会让 OC 目标的引证计数 +1,调用 release 会让 OC 目标的引证计数 -1。

内存办理的经验总结

  • 当调用 alloc、new、copy、mutableCopy 办法回来了一个目标,在不需求这个目标时,要调用 release 或者 autorelease 来开释它

  • 想具有某个目标,就让它的引证计数 +1;不想再具有某个目标,就让它的引证计数 -1

  • 能够经过以下私有函数来检查主动开释池的情况extern void _objc_autoreleasePoolPrint(void);

僵尸目标:重复开释内存形成的。一个典型场景是屡次 setter。setter 内部完成不合理,比方下面 setter。

Person *p = [[Person aloc] init]; // 1
Cat *cat = [[Cat alloc] init]; // 1  
[p setCat:cat];  // 2
[cat release]; // 1
[p setCat:cat]; // 0
[p setCat:cat]; // badAccess
- (void)setCat:(Cat *)cat
{
    [_cat release];
    _cat = [cat retain];
}

改进

- (void)setCat:(Cat *)cat
{    
    if (_cat != cat) {
        [_cat release];
        _cat = [cat retain];
    }
} 

前期在 MRC 年代,在 .h 文件中 @property 只会特点的 getter、setter 声明,@synthesize 会主动生成成员变量和特点的 setter、getter 的完成。跟着编译器前进,现在 @property 会做完全部的作业。

前期 VC 中运用特点

@property (nonatomic, strong) NSMutableDictionary *dict;
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.dict = dict;
[dict release];

经过 Foundation 结构中类办法创立出来的目标,会主动调用 autorelease 办法。

简写为 self.dict = [NSMutableDictionary dictionary];

上述能够检查 GUNStep 源码 NSDictionary.m

#define    AUTORELEASE(object)    [(id)(object) autorelease]
+ (id) dictionary {
  return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()] init]);
}

QA:ARC 做了什么

ARC 其实是 LLVM + Runtime 一起作用的成果。LLVM 编译器主动刺进 retain、release 内存办理代码。Runtime 运转时帮咱们处理相似 __weak 程序运转进程中弱引证铲除去。

copy/mutableCopy

OC 有2个复制办法

  • copy 不可变复制,发生新不可变目标

  • mutableCopy 可变复制,发生新可变目标

上个 Demo1

NSArray *array1 = [[NSArray alloc] initWithObjects:@"1", @"2", nil];
NSLog(@"array1 --- %zd", array1.retainCount);
NSArray *array2 = [array1 copy];
NSLog(@"array1 --- %zd", array1.retainCount);
NSLog(@"array2 --- %zd", array2.retainCount);
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"array1 --- %zd", array1.retainCount);
NSLog(@"array2 --- %zd", array2.retainCount);
NSLog(@"array3 --- %zd" array3.retainCount);
[array3 release];
NSLog(@"array3 --- %zd", array3.retainCount);
[array2 release];
NSLog(@"array2 --- %zd", array2.retainCount);
NSLog(@"array1 --- %zd", array1.retainCount);
[array1 release];
NSLog(@"array1 --- %zd", array1.retainCount);
2022-04-12 20:50:43.639296+0800 Main[4408:60897] array1 --- 1
2022-04-12 20:50:43.639715+0800 Main[4408:60897] array1 --- 2
2022-04-12 20:50:43.639772+0800 Main[4408:60897] array2 --- 2
2022-04-12 20:50:43.639846+0800 Main[4408:60897] array1 --- 2
2022-04-12 20:50:43.639899+0800 Main[4408:60897] array2 --- 2
2022-04-12 20:50:43.639957+0800 Main[4408:60897] array3 --- 1
2022-04-12 20:50:43.640013+0800 Main[4408:60897] array3 --- 0
2022-04-12 20:50:43.640059+0800 Main[4408:60897] array2 --- 1
2022-04-12 20:50:43.640105+0800 Main[4408:60897] array1 --- 1
2022-04-12 20:50:43.640159+0800 Main[4408:60897] array1 --- 0

疑问1: 为什么在 array2 创立之后 array2、array1 的引证技能都是2.

由于 array1 指针指向堆上一块内存(NSArray 类型),创立好后 array1 引证计数为1。在创立 array2 的时分发现是对 array1 的浅复制,体系为了内存的节省优化,array2 的指针也指向堆上的这一块内存,copy 自身会对 array1 引证技能 +1,变为2。所以这时分 array2 指针指向的内存,引证计数也是2.

基于此,咱们稍微修正下,看看 Demo2

NSArray *array1 = [[NSArray alloc] initWithObjects:@"1", @"2", nil];
NSLog(@"array1 --- %zd", array1.retainCount);
NSArray *array2 = [array1 mutableCopy];
NSLog(@"array1 --- %zd", array1.retainCount);
NSLog(@"array2 --- %zd", array2.retainCount);
2022-04-12 20:55:36.539060+0800 Main[4576:65031] array1 --- 1
2022-04-12 20:55:36.539514+0800 Main[4576:65031] array1 --- 1
2022-04-12 20:55:36.539631+0800 Main[4576:65031] array2 --- 1

由于 array1 指针指向堆上一块内存(NSArray 类型),创立好后 array1 引证计数为1。在创立 array2 的时分发现是对 array1 的深复制,要发生不可变目标,所以堆上请求内存空间,array2 指针指向这块内存,引证技能为1。

此外 mutableCopy 是 Foundation 针对调集类供给的。假如自定义目标需求支撑 copy 办法,需遵循对应的NSCopyint 协议,完成协议办法 -(id)copyWithZone:(NSZone *)zone

总结:

NSString NSMutableString NSArray NSMutableArray NSDictionary NSMutableDictionary
copy NSString 浅复制 NSString 深复制 NSArray 浅复制 NSArray 深复制 NSDictionary 浅复制 NSDictionary 深复制
mutableCopy NSMutableString 深复制 NSMutableString 深复制 NSMutableArray 深复制 NSMutableArray 深复制 NSMutableDictionary 深复制 NSMutableDictionary 深复制

引证计数

union isa_t {
    Class cls;
    uintptr_t bits;
    struct {
        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 deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
    };
}

iOS 从 64 位开端开端,对 isa 进行了优化,信息寄存于 union 结构中

  • extra_rc 存储引证计数信息-1,能够看到是 19位。存储引证计数器 -1

  • has_sidetable_rc 引证计数是否过大无法存储在 isa。当过大无法存储与 isa 中时,has_sidetable_rc 这位会变为1,引证计数存储在 SideTable 的类的特点中

也便是说,iOS 从64位开端,引证计数寄存于 isa 结构体的一个 union 中,字段为 extra_rc,值为目标引证计数值 -1。当引证计数过大无法寄存的时分 union 中 has_sidetable_rc 为 1,则引证计数寄存于 SideTable 结构体中。

SideTable 结构如下

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
};

其间 refcnts 是一个寄存着目标引证计数的散列表

检查 objc4 关于引证计数的完成

uintptr_t _objc_rootRetainCount(id obj) {
    assert(obj);
    return obj->rootRetainCount();
}
inline uintptr_t objc_object::rootRetainCount() {
    if (isTaggedPointer()) return (uintptr_t)this;
    sidetable_lock();
    isa_t bits = LoadExclusive(&isa.bits);
    ClearExclusive(&isa.bits);
    if (bits.nonpointer) { // 优化过的 isa
        uintptr_t rc = 1 + bits.extra_rc; 
        if (bits.has_sidetable_rc) { // 引证计数不是存储在 isa 中,而是 SideTable 
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }
    sidetable_unlock();
    return sidetable_retainCount();
}
size_t  objc_object::sidetable_getExtraRC_nolock() {
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this];
    RefcountMap::iterator it = table.refcnts.find(this); // key 拿值
    if (it == table.refcnts.end()) return 0;
    else return it->second >> SIDE_TABLE_RC_SHIFT;
}

__unsafe_unretained 不安全怎样表现?上 Demo

__weak Person *p2;
__unsafe_unretained Person *p3;
{
    Person *p = [[Person alloc] init];
    p2 = p;
}
NSLog(@"%@", p2);
2022-04-12 21:39:30.308917+0800 Main[5307:98296] -[Person dealloc]
2022-04-12 21:39:30.309413+0800 Main[5307:98296] (null)

能够看到出了代码块,之后 p2 尽管指向 p,可是 p 没有强指针指向,所以收回了,此刻打印 p2,是 null。

__unsafe_unretained Person *p3;
{
    Person *p = [[Person alloc] init];
    p3 = p;
}
NSLog(@"%@", p3);
2022-04-12 21:40:47.558581+0800 Main[5342:99598] -[Person dealloc]
2022-04-12 21:40:47.559330+0800 Main[5342:99598] <Person: 0x101206130>

当用 __unsafe_unretained 润饰后,尽管开释了,可是内存还没收回,这时分去运用很容易出错。

dealloc 是怎样作业的?

在 MRC 年代,写完代码都需求显现在 dealloc 办法中做一些内存收回之类的作业。目标析构时将内部目标先 release 掉,非 OC 目标(比方定时器、c 目标、CF 目标等) 也需求收回内存,最终调用 [super dealloc] 持续将父类目标做析构。

- (void)dealloc {
    CFRelease(XX);
    self.timer = nil;
    [super dealloc];
}

但在 ARC 年代,dealloc 中一般只需求写一些非 OC 目标的内存开释作业,比方 CFRelease()

带来2个问题:

  • 类中的实例变量在哪开释?

  • 当时类中没有显现调用 [super dealloc] ,父类的析构怎样触发?

LLVM 文档对 dealloc 的描述

LLVM ARC 文档对 dealloc 描述 如下

A class may provide a method definition for an instance method named dealloc. This method will be called after the final release of the object but before it is deallocated or any of its instance variables are destroyed. The superclass’s implementation of dealloc will be called automatically when the method returns.

The instance variables for an ARC-compiled class will be destroyed at some point after control enters the dealloc method for the root class of the class. The ordering of the destruction of instance variables is unspecified, both within a single class and between subclasses and superclasses.

依据描述能够看到 dealloc 办法在最终一次 release 办法调用后触发,但实例变量(ivars) 还未开释,父类的 dealloc 办法将会在子类 dealloc 办法回来后主动调用。

ARC 形式下,目标的实例变量会在根类 [NSObject dealloc] 中开释,可是开释的顺序是不一定的。

也便是说会主动调用 [super dealloc],那到底怎样完成的,探求下。

检查 objc4 源码

- (void)dealloc {
    _objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj) {
    assert(obj);
    obj->rootDealloc();
}
inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return;  // fixme necessary?
    // fastpath 判别当时目标是否满意条件。
    if (fastpath(isa.nonpointer  &&      // nonpointer
                 !isa.weakly_referenced  &&   // 是否有弱引证
                 !isa.has_assoc  &&   // 相关目标
                 !isa.has_cxx_dtor  &&   // c++ 析构函数
                 !isa.has_sidetable_rc)) // 是否有 SideTable
    {
        assert(!sidetable_present());
        free(this);
    }  else {
        object_dispose((id)this);
    }
}
id object_dispose(id obj){
    if (!obj) return nil;
    objc_destructInstance(obj);    
    free(obj);
    return nil;
}
void *objc_destructInstance(id obj) {
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        // This order is important.
        if (cxx) object_cxxDestruct(obj); // 铲除成员变量
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating(); // 将指向当时目标的弱指针置为 nil
    }
    return obj;
}
inline void objc_object::clearDeallocating() {
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}
void objc_object::sidetable_clearDeallocating(){
    SideTable& table = SideTables()[this];
    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        table.refcnts.erase(it);
    }
    table.unlock();
}
void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }
    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    weak_entry_remove(weak_table, entry);
}

能够清楚看到在 objc_destructInstance 办法中调用了3个核心办法

  • object_cxxDestruct(obj): 铲除成员变量

  • object_remove_assocations(obj):去除该目标相关的相关特点(Category 添加的)

  • obj->clearDeallocating():清空引证技能表和弱引证表,将 weak 引证设置为 nil

持续看看 object_cxxDestruct 办法内部细节。

神秘的 cxx_destruct

object_cxxDestruct 办法终究会调用到 object_cxxDestructFromClass

void object_cxxDestruct(id obj) {
    if (_objc_isTaggedPointerOrNil(obj)) return;
    object_cxxDestructFromClass(obj, obj->ISA());
}
static void object_cxxDestructFromClass(id obj, Class cls) {
    void (*dtor)(id);
    // Call cls's dtor first, then superclasses's dtors.
    for ( ; cls; cls = cls->getSuperclass()) {
        if (!cls->hasCxxDtor()) return; 
        dtor = (void(*)(id))
            lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
        // 调用
        if (dtor != (void(*)(id))_objc_msgForward_impcache) {
            if (PrintCxxCtors) {
                _objc_inform("CXX: calling C++ destructors for class %s", 
                             cls->nameForLogging());
            }
            (*dtor)(obj);
        }
    }
}

做的作业便是遍历,不断寻找父类中 SEL_cxx_destruct这个 selector,找到函数完成并调用。

void sel_init(size_t selrefCount){
#if SUPPORT_PREOPT
    if (PrintPreopt) {
        _objc_inform("PREOPTIMIZATION: using dyld selector opt");
    }
#endif
  namedSelectors.init((unsigned)selrefCount);
    // Register selectors used by libobjc
    mutex_locker_t lock(selLock)
    SEL_cxx_construct = sel_registerNameNoLock(".cxx_construct", NO);
    SEL_cxx_destruct = sel_registerNameNoLock(".cxx_destruct", NO);
}

持续翻阅源码发现 SEL_cxx_destruct 其实便是 .cxx_destruct。在 《Effective Objective-C 2.0》中阐明:

When the compiler saw that an object contained C++ objects, it would generate a method called .cxx_destruct. ARC piggybacks on this method and emits the required cleanup code within it.

也便是说,当编译器看到 C++ 目标的时分,它将会生成 .cxx_destruct 析构办法,可是 ARC 借用这个办法,并在其间刺进了代码以完成主动内存开释的功用。

探求啥时分生成 .cxx_destruct 办法

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end
// 
- (void)viewDidLoad {
    [super viewDidLoad];
    {
        NSLog(@"comes");
        Person *p = [[Person alloc] init];
        p.name = @"杭城小刘";
        NSLog(@"gone");
    }
}

在 gone 处加断点,运用 runtime 检查类中的办法信息

发现存在 .cxx_destruct 办法。

咱们一开要研讨的是 ivars 啥时分开释,所以操控变量,将特点改为成员目标

@interface Person : NSObject
{
    @public
    NSString *name;
}
@end
{
    NSLog(@"comes");
    Person *p = [[Person alloc] init];
    p->name = @"杭城小刘";
    NSLog(@"gone");
}

也有 .cxx_destruct 办法

将成员变量换为根本数据类型

@interface Person : NSObject
{
    @public
    int age;
}
@end

Tips:@property 会主动生成成员变量,别的类后边加 {} 在内部也能够加成员变量,假设成员变量是目标类型,比方 NSString,则叫实例变量。

得出结论:

  • 只要 ARC 形式下才有 .cxx_destruct 办法

  • 类具有实例变量的时分({} 或者 @property) 才有 .cxx_destruct,父类成员目标的实例变量不会让子类具有该办法

运用 watchpoint 观察内存开释时机

在 gone 的当地加断点,输入 watchpoint set variable p->_name,则会将 _name 实例变量加入 watchpoint,当变量被修正时会触发断点,能够看出从某个值变为 0x0,也便是 nil。此刻边上调用仓库显现在 objc_storestrong 办法中,被设置为 nil.

深入 .cxx_destruct

简略整理下,在 ARC 形式下,类具有实例变量的时分会在 .cxx_destruct 办法内调用 objc_storeStrong 去开释的内存。

咱们也知道 .cxx_destruct 是编译器生成的代码。去查询材料 .cxx_destruct site:clang.llvm.org

在 clang 的 doxygen 文档中 CodeGenModule 模块源码发现了相关逻辑。在 5907 行代码

void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) {
  // We might need a .cxx_destruct even if we don't have any ivar initializers.
  if (needsDestructMethod(D)) {
    IdentifierInfo *II = &getContext().Idents.get(".cxx_destruct");
    Selector cxxSelector = getContext().Selectors.getSelector(0, &II);
    ObjCMethodDecl *DTORMethod = ObjCMethodDecl::Create(
        getContext(), D->getLocation(), D->getLocation(), cxxSelector,
        getContext().VoidTy, nullptr, D,
        /*isInstance=*/true, /*isVariadic=*/false,
        /*isPropertyAccessor=*/true, /*isSynthesizedAccessorStub=*/false,
        /*isImplicitlyDeclared=*/true,
        /*isDefined=*/false, ObjCMethodDecl::Required);
    D->addInstanceMethod(DTORMethod);
    CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false);
    D->setHasDestructors(true);
  }
  // If the implementation doesn't have any ivar initializers, we don't need
  // a .cxx_construct.
  if (D->getNumIvarInitializers() == 0 ||
      AllTrivialInitializers(*this, D))
    return;
  IdentifierInfo *II = &getContext().Idents.get(".cxx_construct");
  Selector cxxSelector = getContext().Selectors.getSelector(0, &II);
  // The constructor returns 'self'.
  ObjCMethodDecl *CTORMethod = ObjCMethodDecl::Create(
      getContext(), D->getLocation(), D->getLocation(), cxxSelector,
      getContext().getObjCIdType(), nullptr, D, /*isInstance=*/true,
      /*isVariadic=*/false,
      /*isPropertyAccessor=*/true, /*isSynthesizedAccessorStub=*/false,
      /*isImplicitlyDeclared=*/true,
      /*isDefined=*/false, ObjCMethodDecl::Required);
  D->addInstanceMethod(CTORMethod);
  CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true);
  D->setHasNonZeroConstructors(true);
}

源码大约做的作业便是:获取 .cxx_destructor 的 selector,创立 Method,然后将新创立的 Method 刺进到 class 办法列表中。调用 GenerateObjCCtorDtorMethod 办法,才创立这个办法的完成。检查 GenerateObjCCtorDtorMethod 的完成。在 clang.llvm.org/doxygen/CGO… 的1626行处。

static void emitCXXDestructMethod(CodeGenFunction &CGF,
                                   ObjCImplementationDecl *impl) {
   CodeGenFunction::RunCleanupsScope scope(CGF);
   llvm::Value *self = CGF.LoadObjCSelf();
   const ObjCInterfaceDecl *iface = impl->getClassInterface();
   for (const ObjCIvarDecl *ivar = iface->all_declared_ivar_begin();
        ivar; ivar = ivar->getNextIvar()) {
     QualType type = ivar->getType();
     // Check whether the ivar is a destructible type.
     QualType::DestructionKind dtorKind = type.isDestructedType();
     if (!dtorKind) continue;
     CodeGenFunction::Destroyer *destroyer = nullptr;
     // Use a call to objc_storeStrong to destroy strong ivars, for the
     // general benefit of the tools.
     if (dtorKind == QualType::DK_objc_strong_lifetime) {
       destroyer = destroyARCStrongWithStore;
     // Otherwise use the default for the destruction kind.
     } else {
       destroyer = CGF.getDestroyer(dtorKind);
     }
     CleanupKind cleanupKind = CGF.getCleanupKind(dtorKind);
     CGF.EHStack.pushCleanup<DestroyIvar>(cleanupKind, self, ivar, destroyer,
                                          cleanupKind & EHCleanup);
   }
   assert(scope.requiresCleanups() && "nothing to do in .cxx_destruct?");
 }

能够看到:遍历了当时目标的一切实例变量,调用 objc_storeStrong,从 clang 文档上能够看出

id objc_storeStrong(id *object, id value) {
  value = [value retain];
  id oldValue = *object;
  *object = value;
  [oldValue release];
  return value;
}

.cxx_destruct 办法内部会对一切的实例变量调用 objc_storeStrong(&ivar, null) ,实例变量就会 release 。

主动调用 [super dealloc] 的原理

同理,CodeGen 也会做主动调用 [super dealloc] 的作业。clang.llvm.org/doxygen/CGO… StartObjCMethod 办法。

  751 void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD,
  752                                       const ObjCContainerDecl *CD) {
  // ...  
  789   // In ARC, certain methods get an extra cleanup.
  790   if (CGM.getLangOpts().ObjCAutoRefCount &&
  791       OMD->isInstanceMethod() &&
  792       OMD->getSelector().isUnarySelector()) {
  793     const IdentifierInfo *ident =
  794       OMD->getSelector().getIdentifierInfoForSlot(0);
  795     if (ident->isStr("dealloc"))
  796       EHStack.pushCleanup<FinishARCDealloc>(getARCCleanupKind());
  797   }
  798 }

能够看到在调用到 dealloc 办法时,刺进了代码,完成如下

struct FinishARCDealloc : EHScopeStack::Cleanup {
   void Emit(CodeGenFunction &CGF, Flags flags) override {
     const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CGF.CurCodeDecl);
     const ObjCImplDecl *impl = cast<ObjCImplDecl>(method->getDeclContext());
     const ObjCInterfaceDecl *iface = impl->getClassInterface();
     if (!iface->getSuperClass()) return;
     bool isCategory = isa<ObjCCategoryImplDecl>(impl);
     // Call [super dealloc] if we have a superclass.
     llvm::Value *self = CGF.LoadObjCSelf();
     CallArgList args;
     CGF.CGM.getObjCRuntime().GenerateMessageSendSuper(CGF, ReturnValueSlot(),
                                                       CGF.getContext().VoidTy,
                                                       method->getSelector(),
                                                       iface,
                                                       isCategory,
                                                       self,
                                                       /*is class msg*/ false,
                                                       args,
                                                       method);
   }
};

代码大约便是向父类转发 dealloc 的调用完成,内部主动调用 [super dealloc] 办法。

总结下:

  • ARC 形式下,实例变量由编译器刺进 .cxx_destruct 办法主动开释

  • ARC 形式下 [super dealloc] 由 llvm 编译器主动刺进(CodeGen)

AutoreleasePool 底层原理探究

单 AutoreleasePool 的 case

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
    }
    return 0;
}

clang 转为 c++ xcrun -sdk iphonesimulator clang -rewrite-objc main.m

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ {  __AtAutoreleasePool __autoreleasepool; 
        Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
    }
    return 0;
}

下面的代码其实便是 objc_msgSend,有用代码是 __AtAutoreleasePool __autoreleasepool;

持续查找

struct __AtAutoreleasePool {
    __AtAutoreleasePool() {
    atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    ~__AtAutoreleasePool() {
    objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
};

OC 目标实质便是结构体

  • __AtAutoreleasePool 结构体中 __AtAutoreleasePool 是结构办法,在创立结构体的时分调用

  • ~__AtAutoreleasePool 是析构函数,在结构体毁掉的时分调用

main 内的代码作用域,离开代表毁掉。所以上面代码等价于

atautoreleasepoolobj = objc_autoreleasePoolPush();
Person *p = [[[Person alloc] init] autorelease];
objc_autoreleasePoolPop(atautoreleasepoolobj);

运用关键函数持续检查 objc4 源码

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

主动开释池的主要完成依托2个目标:__AtAutoreleasePoolAutoreleasePoolPage

objc_autoreleasePoolPush、objc_autoreleasePoolPop 底层都是调用了 AutoreleasePoolPage 目标来办理的。

检查源码

class AutoreleasePoolPage {
    magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
}
  • 每个 AutoreleasePoolPage 目标占用 4096 字节内存,除了用来寄存它内部的成员变量,剩下的空间用来寄存 autorelease 目标的地址
  • 一切的 AutoreleasePoolPage 目标经过双向链表的形式衔接在一起。child 指向下一个目标,parent 指向上一个目标

剖析 iOS 内存原理

id * begin() {
    return (id *) ((uint8_t *)this+sizeof(*this));
}
id * end() {
    return (id *) ((uint8_t *)this+SIZE);
}

其间 begin 办法回来 autoreleasePoolPage 目标中开端存储 autorelease 目标的开端地址

end 办法回来 autoreleasePoolPage 目标中完毕存储 autorelease 目标的开端地址

调用 AutoreleasePoolPage::push 办法会将一个 POOL_BOUNDARY 入栈,并且回来其寄存的内存地址

调用 AutoreleasePoolPage::pop 办法时传入一个 POOL_BOUNDARY 的内存地址,体系会从最终一个入栈的目标开端发送 release消 息,直到遇到这个 POOL_BOUNDARY

id *next 指向了下一个能寄存 autorelease 目标地址的区域

static inline void *push() {
    id *dest;
    if (DebugPoolAllocation) {
        // Each autorelease pool starts on a new pool page.
        dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    return dest;
}

多 AutoreleasePool 的 case

来个骚一些的比如

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        @autoreleasepool {
            Person *p3 = [[[Person alloc] init] autorelease];
            @autoreleasepool {
                Person *p4 = [[[Person alloc] init] autorelease];
            }
        }
    }
    return 0;
}

main 办法内部3个 autoreleasepool 底层怎样样作业的?

剖析 iOS 内存原理

3个@auto releasepool, 体系遇到第一个的时分底层便是初始化一个结构体 __AtAutoreleasePool,结构体结构办法内部调用 AutoreleasePoolPage::push 办法,体系给 AutoreleasePoolPage 实在保存 autorelease 目标的当地存储进一个 POOL_BOUNDARY 目标,然后贮存 P1、P2 目标地址,遇到第二个则持续初始化结构体,调用 push 办法,存储一个 POOL_BOUNDARY 目标,持续保存 P3,遇到第三个则持续初始化结构体,调用 push 办法,存储一个 POOL_BOUNDARY 目标,持续保存 P4。

当完毕第三个大括号的时分,第三个结构体目标,调用析构函数,内部调用 AutoreleasePoolPage::pop 办法,会从最终一个入栈的目标开端发送 release 音讯,直到遇到 POOL_BOUNDARY 目标。

紧接着第二个大括号完毕,第二个结构体目标析构函数履行,内部调用 AutoreleasePoolPage::pop 办法,会从最终一个入栈的目标开端发送 release 音讯,直到遇到 POOL_BOUNDARY 目标。

所以,嵌套的AutoreleasePool就十分简略了,pop的时分总会开释到前次push的位置停止,多层的pool便是多个岗兵目标而已,就像剥洋葱一样,每次一层,互不影响

第一个同理。

小窍门,对于上述原理的剖析能够用源码中看到的 AutoreleasePoolPage 目标的 printAll 办法。

static void printAll() {
    _objc_inform("##############");
    _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
    AutoreleasePoolPage *page;
    ptrdiff_t objects = 0;
    for (page = coldPage(); page; page = page->child) {
        objects += page->next - page->begin();
    }
    _objc_inform("%llu releases pending.", (unsigned long long)objects);
    if (haveEmptyPoolPlaceholder()) {
        _objc_inform("[%p]  ................  PAGE (placeholder)", 
                        EMPTY_POOL_PLACEHOLDER);
        _objc_inform("[%p]  ################  POOL (placeholder)", 
                        EMPTY_POOL_PLACEHOLDER);
    }
    else {
        for (page = coldPage(); page; page = page->child) {
            page->print();
        }
    }
    _objc_inform("##############");
}
void _objc_autoreleasePoolPrint(void) {
    AutoreleasePoolPage::printAll();
}

查了下 printAll 函数的运用方,就只要 _objc_autoreleasePoolPrint 函数。且能够看到在 objc4 objc-internal.h 头文件中有将该函数 export 出去,也便是能够在外部链接该符号。

OBJC_EXPORT void _objc_autoreleasePoolPrint(void) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);

所以咱们在测验 Demo 中将 _objc_autoreleasePoolPrint 函数声明下。在打印下

extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        @autoreleasepool {
            Person *p3 = [[[Person alloc] init] autorelease];
            @autoreleasepool {
                Person *p4 = [[[Person alloc] init] autorelease];
                _objc_autoreleasePoolPrint();
            }
        }
    }
    return 0;
}
objc[23132]: ##############
objc[23132]: AUTORELEASE POOLS for thread 0x100094600
objc[23132]: 7 releases pending.
objc[23132]: [0x10080a000]  ................  PAGE  (hot) (cold)
objc[23132]: [0x10080a038]  ################  POOL 0x10080a038
objc[23132]: [0x10080a040]       0x10075f060  Person
objc[23132]: [0x10080a048]       0x10075f0c0  Person
objc[23132]: [0x10080a050]  ################  POOL 0x10080a050
objc[23132]: [0x10080a058]       0x10075f0e0  Person
objc[23132]: [0x10080a060]  ################  POOL 0x10080a060
objc[23132]: [0x10080a068]       0x10075f100  Person
objc[23132]: ##############

能够看到打印成果和上面的剖析是一起的(和上面的图片比照看看)

再来个 Demo,验证下 AutoreleasePoolPage 一页满情况

extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        @autoreleasepool {
            for (NSInteger index = 0; index<600; index++) {
                Person *p3 = [[[Person alloc] init] autorelease];
            }
            @autoreleasepool {
                Person *p4 = [[[Person alloc] init] autorelease];
                _objc_autoreleasePoolPrint();
            }
        }
    }
    return 0;
}
objc[23504]: ##############
objc[23504]: AUTORELEASE POOLS for thread 0x100094600
objc[23504]: 606 releases pending.
objc[23504]: [0x10080d000]  ................  PAGE (full)  (cold)
objc[23504]: [0x10080d038]  ################  POOL 0x10080d038
objc[23504]: [0x10080d040]       0x1007092f0  Person
objc[23504]: [0x10080d048]       0x100709350  Person
objc[23504]: [0x10080d050]  ################  POOL 0x10080d050
objc[23504]: [0x10080d058]       0x100753250  Person
objc[23504]: [0x10080d060]       0x100753270  Person
objc[23504]: [0x10080d068]       0x100753290  Person
objc[23504]: [0x10080d070]       0x1007532b0  Person
objc[23504]: [0x10080d078]       0x1007532d0  Person
objc[23504]: [0x10080d080]       0x1007532f0  Person
objc[23504]: [0x10080d088]       0x100753310  Person
objc[23504]: [0x10080d090]       0x100753330  Person
objc[23504]: [0x10080d098]       0x100753680  Person
objc[23504]: [0x10080d0a0]       0x1007536a0  Person
objc[23504]: [0x10080d0a8]       0x1007536c0  Person
objc[23504]: [0x10080d0b0]       0x1007536e0  Person
objc[23504]: [0x10080d0b8]       0x100753700  Person
objc[23504]: [0x10080d0c0]       0x100753720  Person
objc[23504]: [0x10080d0c8]       0x100753740  Person
objc[23504]: [0x10080d0d0]       0x100753760  Person
objc[23504]: [0x10080d0d8]       0x100753780  Person
objc[23504]: [0x10080d0e0]       0x1007537a0  Person
objc[23504]: [0x10080d0e8]       0x1007537c0  Person
objc[23504]: [0x10080d0f0]       0x1007537e0  Person
objc[23504]: [0x10080d0f8]       0x100753800  Person
objc[23504]: [0x10080d100]       0x100753820  Person
objc[23504]: [0x10080d108]       0x100753840  Person
objc[23504]: [0x10080d110]       0x100753860  Person
objc[23504]: [0x10080d118]       0x100753880  Person
objc[23504]: [0x10080d120]       0x1007538a0  Person
objc[23504]: [0x10080d128]       0x1007538c0  Person
objc[23504]: [0x10080d130]       0x1007538e0  Person
objc[23504]: [0x10080d138]       0x100753900  Person
objc[23504]: [0x10080d140]       0x100753920  Person
objc[23504]: [0x10080d148]       0x100753940  Person
objc[23504]: [0x10080d150]       0x100753960  Person
objc[23504]: [0x10080d158]       0x100753980  Person
objc[23504]: [0x10080d160]       0x1007539a0  Person
objc[23504]: [0x10080d168]       0x1007539c0  Person
objc[23504]: [0x10080d170]       0x1007539e0  Person
objc[23504]: [0x10080d178]       0x100753a00  Person
objc[23504]: [0x10080d180]       0x100753a20  Person
objc[23504]: [0x10080d188]       0x100753a40  Person
objc[23504]: [0x10080d190]       0x100753a60  Person
objc[23504]: [0x10080d198]       0x100753a80  Person
objc[23504]: [0x10080d1a0]       0x100753aa0  Person
objc[23504]: [0x10080d1a8]       0x100753ac0  Person
objc[23504]: [0x10080d1b0]       0x100753ae0  Person
objc[23504]: [0x10080d1b8]       0x100753b00  Person
objc[23504]: [0x10080d1c0]       0x100753b20  Person
objc[23504]: [0x10080d1c8]       0x100753b40  Person
objc[23504]: [0x10080d1d0]       0x100753b60  Person
objc[23504]: [0x10080d1d8]       0x100753b80  Person
objc[23504]: [0x10080d1e0]       0x100753ba0  Person
objc[23504]: [0x10080d1e8]       0x100753bc0  Person
objc[23504]: [0x10080d1f0]       0x100753be0  Person
objc[23504]: [0x10080d1f8]       0x100753c00  Person
objc[23504]: [0x10080d200]       0x100753c20  Person
objc[23504]: [0x10080d208]       0x100753c40  Person
objc[23504]: [0x10080d210]       0x100753c60  Person
objc[23504]: [0x10080d218]       0x100753c80  Person
objc[23504]: [0x10080d220]       0x100753ca0  Person
objc[23504]: [0x10080d228]       0x100753cc0  Person
objc[23504]: [0x10080d230]       0x100753ce0  Person
objc[23504]: [0x10080d238]       0x100753d00  Person
objc[23504]: [0x10080d240]       0x100753d20  Person
objc[23504]: [0x10080d248]       0x100753d40  Person
objc[23504]: [0x10080d250]       0x100753d60  Person
objc[23504]: [0x10080d258]       0x100753d80  Person
objc[23504]: [0x10080d260]       0x100753da0  Person
objc[23504]: [0x10080d268]       0x100753dc0  Person
objc[23504]: [0x10080d270]       0x100753de0  Person
objc[23504]: [0x10080d278]       0x100753e00  Person
objc[23504]: [0x10080d280]       0x100753e20  Person
objc[23504]: [0x10080d288]       0x100753e40  Person
objc[23504]: [0x10080d290]       0x100753e60  Person
objc[23504]: [0x10080d298]       0x100753e80  Person
objc[23504]: [0x10080d2a0]       0x100753ea0  Person
objc[23504]: [0x10080d2a8]       0x100753ec0  Person
objc[23504]: [0x10080d2b0]       0x100753ee0  Person
objc[23504]: [0x10080d2b8]       0x100753f00  Person
objc[23504]: [0x10080d2c0]       0x100753f20  Person
objc[23504]: [0x10080d2c8]       0x100753f40  Person
objc[23504]: [0x10080d2d0]       0x100753f60  Person
objc[23504]: [0x10080d2d8]       0x100753f80  Person
objc[23504]: [0x10080d2e0]       0x100753fa0  Person
objc[23504]: [0x10080d2e8]       0x100753fc0  Person
objc[23504]: [0x10080d2f0]       0x100753fe0  Person
objc[23504]: [0x10080d2f8]       0x100754000  Person
objc[23504]: [0x10080d300]       0x100754020  Person
objc[23504]: [0x10080d308]       0x100754040  Person
objc[23504]: [0x10080d310]       0x100754060  Person
objc[23504]: [0x10080d318]       0x100754080  Person
objc[23504]: [0x10080d320]       0x1007540a0  Person
objc[23504]: [0x10080d328]       0x1007540c0  Person
objc[23504]: [0x10080d330]       0x1007540e0  Person
objc[23504]: [0x10080d338]       0x100754100  Person
objc[23504]: [0x10080d340]       0x100754120  Person
objc[23504]: [0x10080d348]       0x100754140  Person
objc[23504]: [0x10080d350]       0x100754160  Person
objc[23504]: [0x10080d358]       0x100754180  Person
objc[23504]: [0x10080d360]       0x1007541a0  Person
objc[23504]: [0x10080d368]       0x1007541c0  Person
objc[23504]: [0x10080d370]       0x1007541e0  Person
objc[23504]: [0x10080d378]       0x100754200  Person
objc[23504]: [0x10080d380]       0x100754220  Person
objc[23504]: [0x10080d388]       0x100754240  Person
objc[23504]: [0x10080d390]       0x100754260  Person
objc[23504]: [0x10080d398]       0x100754280  Person
objc[23504]: [0x10080d3a0]       0x1007542a0  Person
objc[23504]: [0x10080d3a8]       0x1007542c0  Person
objc[23504]: [0x10080d3b0]       0x1007542e0  Person
objc[23504]: [0x10080d3b8]       0x100754300  Person
objc[23504]: [0x10080d3c0]       0x100754320  Person
objc[23504]: [0x10080d3c8]       0x100754340  Person
objc[23504]: [0x10080d3d0]       0x100754360  Person
objc[23504]: [0x10080d3d8]       0x100754380  Person
objc[23504]: [0x10080d3e0]       0x1007543a0  Person
objc[23504]: [0x10080d3e8]       0x1007543c0  Person
objc[23504]: [0x10080d3f0]       0x1007543e0  Person
objc[23504]: [0x10080d3f8]       0x100754400  Person
objc[23504]: [0x10080d400]       0x100754420  Person
objc[23504]: [0x10080d408]       0x100754440  Person
objc[23504]: [0x10080d410]       0x100754460  Person
objc[23504]: [0x10080d418]       0x100754480  Person
objc[23504]: [0x10080d420]       0x1007544a0  Person
objc[23504]: [0x10080d428]       0x1007544c0  Person
objc[23504]: [0x10080d430]       0x1007544e0  Person
objc[23504]: [0x10080d438]       0x100754500  Person
objc[23504]: [0x10080d440]       0x100754520  Person
objc[23504]: [0x10080d448]       0x100754540  Person
objc[23504]: [0x10080d450]       0x100754560  Person
objc[23504]: [0x10080d458]       0x100754580  Person
objc[23504]: [0x10080d460]       0x1007545a0  Person
objc[23504]: [0x10080d468]       0x1007545c0  Person
objc[23504]: [0x10080d470]       0x1007545e0  Person
objc[23504]: [0x10080d478]       0x100754600  Person
objc[23504]: [0x10080d480]       0x100754620  Person
objc[23504]: [0x10080d488]       0x100754640  Person
objc[23504]: [0x10080d490]       0x100754660  Person
objc[23504]: [0x10080d498]       0x100754680  Person
objc[23504]: [0x10080d4a0]       0x1007546a0  Person
objc[23504]: [0x10080d4a8]       0x1007546c0  Person
objc[23504]: [0x10080d4b0]       0x1007546e0  Person
objc[23504]: [0x10080d4b8]       0x100754700  Person
objc[23504]: [0x10080d4c0]       0x100754720  Person
objc[23504]: [0x10080d4c8]       0x100754740  Person
objc[23504]: [0x10080d4d0]       0x100754760  Person
objc[23504]: [0x10080d4d8]       0x100754780  Person
objc[23504]: [0x10080d4e0]       0x1007547a0  Person
objc[23504]: [0x10080d4e8]       0x1007547c0  Person
objc[23504]: [0x10080d4f0]       0x1007547e0  Person
objc[23504]: [0x10080d4f8]       0x100754800  Person
objc[23504]: [0x10080d500]       0x100754820  Person
objc[23504]: [0x10080d508]       0x100754840  Person
objc[23504]: [0x10080d510]       0x100754860  Person
objc[23504]: [0x10080d518]       0x100754880  Person
objc[23504]: [0x10080d520]       0x1007548a0  Person
objc[23504]: [0x10080d528]       0x1007548c0  Person
objc[23504]: [0x10080d530]       0x1007548e0  Person
objc[23504]: [0x10080d538]       0x100754900  Person
objc[23504]: [0x10080d540]       0x100754920  Person
objc[23504]: [0x10080d548]       0x100754940  Person
objc[23504]: [0x10080d550]       0x100754960  Person
objc[23504]: [0x10080d558]       0x100754980  Person
objc[23504]: [0x10080d560]       0x1007549a0  Person
objc[23504]: [0x10080d568]       0x1007549c0  Person
objc[23504]: [0x10080d570]       0x1007549e0  Person
objc[23504]: [0x10080d578]       0x100754a00  Person
objc[23504]: [0x10080d580]       0x100754a20  Person
objc[23504]: [0x10080d588]       0x100754a40  Person
objc[23504]: [0x10080d590]       0x100754a60  Person
objc[23504]: [0x10080d598]       0x100754a80  Person
objc[23504]: [0x10080d5a0]       0x100754aa0  Person
objc[23504]: [0x10080d5a8]       0x100754ac0  Person
objc[23504]: [0x10080d5b0]       0x100754ae0  Person
objc[23504]: [0x10080d5b8]       0x100754b00  Person
objc[23504]: [0x10080d5c0]       0x100754b20  Person
objc[23504]: [0x10080d5c8]       0x100754b40  Person
objc[23504]: [0x10080d5d0]       0x100754b60  Person
objc[23504]: [0x10080d5d8]       0x100754b80  Person
objc[23504]: [0x10080d5e0]       0x100754ba0  Person
objc[23504]: [0x10080d5e8]       0x100754bc0  Person
objc[23504]: [0x10080d5f0]       0x100754be0  Person
objc[23504]: [0x10080d5f8]       0x100754c00  Person
objc[23504]: [0x10080d600]       0x100754c20  Person
objc[23504]: [0x10080d608]       0x100754c40  Person
objc[23504]: [0x10080d610]       0x100754c60  Person
objc[23504]: [0x10080d618]       0x100754c80  Person
objc[23504]: [0x10080d620]       0x100754ca0  Person
objc[23504]: [0x10080d628]       0x100754cc0  Person
objc[23504]: [0x10080d630]       0x100754ce0  Person
objc[23504]: [0x10080d638]       0x100754d00  Person
objc[23504]: [0x10080d640]       0x100754d20  Person
objc[23504]: [0x10080d648]       0x100754d40  Person
objc[23504]: [0x10080d650]       0x100754d60  Person
objc[23504]: [0x10080d658]       0x100754d80  Person
objc[23504]: [0x10080d660]       0x100754da0  Person
objc[23504]: [0x10080d668]       0x100754dc0  Person
objc[23504]: [0x10080d670]       0x100754de0  Person
objc[23504]: [0x10080d678]       0x100754e00  Person
objc[23504]: [0x10080d680]       0x10074fa70  Person
objc[23504]: [0x10080d688]       0x10074fa90  Person
objc[23504]: [0x10080d690]       0x10074fab0  Person
objc[23504]: [0x10080d698]       0x10074fad0  Person
objc[23504]: [0x10080d6a0]       0x10074faf0  Person
objc[23504]: [0x10080d6a8]       0x10074fb10  Person
objc[23504]: [0x10080d6b0]       0x10074fb30  Person
objc[23504]: [0x10080d6b8]       0x10074fb50  Person
objc[23504]: [0x10080d6c0]       0x10074fb70  Person
objc[23504]: [0x10080d6c8]       0x10074fb90  Person
objc[23504]: [0x10080d6d0]       0x10074fbb0  Person
objc[23504]: [0x10080d6d8]       0x10074fbd0  Person
objc[23504]: [0x10080d6e0]       0x10074fbf0  Person
objc[23504]: [0x10080d6e8]       0x10074fc10  Person
objc[23504]: [0x10080d6f0]       0x10074fc30  Person
objc[23504]: [0x10080d6f8]       0x10074fc50  Person
objc[23504]: [0x10080d700]       0x10074fc70  Person
objc[23504]: [0x10080d708]       0x10074fc90  Person
objc[23504]: [0x10080d710]       0x10074fcb0  Person
objc[23504]: [0x10080d718]       0x10074fcd0  Person
objc[23504]: [0x10080d720]       0x10074fcf0  Person
objc[23504]: [0x10080d728]       0x10074fd10  Person
objc[23504]: [0x10080d730]       0x10074fd30  Person
objc[23504]: [0x10080d738]       0x10074fd50  Person
objc[23504]: [0x10080d740]       0x10074fd70  Person
objc[23504]: [0x10080d748]       0x10074fd90  Person
objc[23504]: [0x10080d750]       0x10074fdb0  Person
objc[23504]: [0x10080d758]       0x10074fdd0  Person
objc[23504]: [0x10080d760]       0x10074fdf0  Person
objc[23504]: [0x10080d768]       0x10074fe10  Person
objc[23504]: [0x10080d770]       0x10074fe30  Person
objc[23504]: [0x10080d778]       0x10074fe50  Person
objc[23504]: [0x10080d780]       0x10074fe70  Person
objc[23504]: [0x10080d788]       0x10074fe90  Person
objc[23504]: [0x10080d790]       0x10074feb0  Person
objc[23504]: [0x10080d798]       0x10074fed0  Person
objc[23504]: [0x10080d7a0]       0x10074fef0  Person
objc[23504]: [0x10080d7a8]       0x10074ff10  Person
objc[23504]: [0x10080d7b0]       0x10074ff30  Person
objc[23504]: [0x10080d7b8]       0x10074ff50  Person
objc[23504]: [0x10080d7c0]       0x10074ff70  Person
objc[23504]: [0x10080d7c8]       0x10074ff90  Person
objc[23504]: [0x10080d7d0]       0x10074ffb0  Person
objc[23504]: [0x10080d7d8]       0x10074ffd0  Person
objc[23504]: [0x10080d7e0]       0x10074fff0  Person
objc[23504]: [0x10080d7e8]       0x100750010  Person
objc[23504]: [0x10080d7f0]       0x100750030  Person
objc[23504]: [0x10080d7f8]       0x100750050  Person
objc[23504]: [0x10080d800]       0x100750070  Person
objc[23504]: [0x10080d808]       0x100750090  Person
objc[23504]: [0x10080d810]       0x1007500b0  Person
objc[23504]: [0x10080d818]       0x1007500d0  Person
objc[23504]: [0x10080d820]       0x1007500f0  Person
objc[23504]: [0x10080d828]       0x100750110  Person
objc[23504]: [0x10080d830]       0x100750130  Person
objc[23504]: [0x10080d838]       0x100750150  Person
objc[23504]: [0x10080d840]       0x100750170  Person
objc[23504]: [0x10080d848]       0x100750190  Person
objc[23504]: [0x10080d850]       0x1007501b0  Person
objc[23504]: [0x10080d858]       0x1007501d0  Person
objc[23504]: [0x10080d860]       0x1007501f0  Person
objc[23504]: [0x10080d868]       0x100750210  Person
objc[23504]: [0x10080d870]       0x100750230  Person
objc[23504]: [0x10080d878]       0x100750250  Person
objc[23504]: [0x10080d880]       0x100750270  Person
objc[23504]: [0x10080d888]       0x100750290  Person
objc[23504]: [0x10080d890]       0x1007502b0  Person
objc[23504]: [0x10080d898]       0x1007502d0  Person
objc[23504]: [0x10080d8a0]       0x1007502f0  Person
objc[23504]: [0x10080d8a8]       0x100750310  Person
objc[23504]: [0x10080d8b0]       0x100750330  Person
objc[23504]: [0x10080d8b8]       0x100750350  Person
objc[23504]: [0x10080d8c0]       0x100750370  Person
objc[23504]: [0x10080d8c8]       0x100750390  Person
objc[23504]: [0x10080d8d0]       0x1007503b0  Person
objc[23504]: [0x10080d8d8]       0x1007503d0  Person
objc[23504]: [0x10080d8e0]       0x1007503f0  Person
objc[23504]: [0x10080d8e8]       0x100750410  Person
objc[23504]: [0x10080d8f0]       0x100750430  Person
objc[23504]: [0x10080d8f8]       0x100750450  Person
objc[23504]: [0x10080d900]       0x100750470  Person
objc[23504]: [0x10080d908]       0x100750490  Person
objc[23504]: [0x10080d910]       0x1007504b0  Person
objc[23504]: [0x10080d918]       0x1007504d0  Person
objc[23504]: [0x10080d920]       0x1007504f0  Person
objc[23504]: [0x10080d928]       0x100750510  Person
objc[23504]: [0x10080d930]       0x100750530  Person
objc[23504]: [0x10080d938]       0x100750550  Person
objc[23504]: [0x10080d940]       0x100750570  Person
objc[23504]: [0x10080d948]       0x100750590  Person
objc[23504]: [0x10080d950]       0x1007505b0  Person
objc[23504]: [0x10080d958]       0x1007505d0  Person
objc[23504]: [0x10080d960]       0x1007505f0  Person
objc[23504]: [0x10080d968]       0x100750610  Person
objc[23504]: [0x10080d970]       0x100750630  Person
objc[23504]: [0x10080d978]       0x100750650  Person
objc[23504]: [0x10080d980]       0x100750670  Person
objc[23504]: [0x10080d988]       0x100750690  Person
objc[23504]: [0x10080d990]       0x1007506b0  Person
objc[23504]: [0x10080d998]       0x1007506d0  Person
objc[23504]: [0x10080d9a0]       0x1007506f0  Person
objc[23504]: [0x10080d9a8]       0x100750710  Person
objc[23504]: [0x10080d9b0]       0x100750730  Person
objc[23504]: [0x10080d9b8]       0x100750750  Person
objc[23504]: [0x10080d9c0]       0x100750770  Person
objc[23504]: [0x10080d9c8]       0x100750790  Person
objc[23504]: [0x10080d9d0]       0x1007507b0  Person
objc[23504]: [0x10080d9d8]       0x1007507d0  Person
objc[23504]: [0x10080d9e0]       0x1007507f0  Person
objc[23504]: [0x10080d9e8]       0x100750810  Person
objc[23504]: [0x10080d9f0]       0x100750830  Person
objc[23504]: [0x10080d9f8]       0x100750850  Person
objc[23504]: [0x10080da00]       0x100750870  Person
objc[23504]: [0x10080da08]       0x100750890  Person
objc[23504]: [0x10080da10]       0x1007508b0  Person
objc[23504]: [0x10080da18]       0x1007508d0  Person
objc[23504]: [0x10080da20]       0x1007508f0  Person
objc[23504]: [0x10080da28]       0x100750910  Person
objc[23504]: [0x10080da30]       0x100750930  Person
objc[23504]: [0x10080da38]       0x100750950  Person
objc[23504]: [0x10080da40]       0x100750970  Person
objc[23504]: [0x10080da48]       0x100750990  Person
objc[23504]: [0x10080da50]       0x1007509b0  Person
objc[23504]: [0x10080da58]       0x1007509d0  Person
objc[23504]: [0x10080da60]       0x1007509f0  Person
objc[23504]: [0x10080da68]       0x100750a10  Person
objc[23504]: [0x10080da70]       0x100750a30  Person
objc[23504]: [0x10080da78]       0x100750a50  Person
objc[23504]: [0x10080da80]       0x100750a70  Person
objc[23504]: [0x10080da88]       0x100750a90  Person
objc[23504]: [0x10080da90]       0x100750ab0  Person
objc[23504]: [0x10080da98]       0x100750ad0  Person
objc[23504]: [0x10080daa0]       0x100750af0  Person
objc[23504]: [0x10080daa8]       0x100750b10  Person
objc[23504]: [0x10080dab0]       0x100750b30  Person
objc[23504]: [0x10080dab8]       0x100750b50  Person
objc[23504]: [0x10080dac0]       0x100750b70  Person
objc[23504]: [0x10080dac8]       0x100750b90  Person
objc[23504]: [0x10080dad0]       0x100750bb0  Person
objc[23504]: [0x10080dad8]       0x100750bd0  Person
objc[23504]: [0x10080dae0]       0x100750bf0  Person
objc[23504]: [0x10080dae8]       0x100750c10  Person
objc[23504]: [0x10080daf0]       0x100750c30  Person
objc[23504]: [0x10080daf8]       0x100750c50  Person
objc[23504]: [0x10080db00]       0x100750c70  Person
objc[23504]: [0x10080db08]       0x100750c90  Person
objc[23504]: [0x10080db10]       0x100750cb0  Person
objc[23504]: [0x10080db18]       0x100750cd0  Person
objc[23504]: [0x10080db20]       0x100750cf0  Person
objc[23504]: [0x10080db28]       0x100750d10  Person
objc[23504]: [0x10080db30]       0x100750d30  Person
objc[23504]: [0x10080db38]       0x100750d50  Person
objc[23504]: [0x10080db40]       0x100750d70  Person
objc[23504]: [0x10080db48]       0x100750d90  Person
objc[23504]: [0x10080db50]       0x100750db0  Person
objc[23504]: [0x10080db58]       0x100750dd0  Person
objc[23504]: [0x10080db60]       0x100750df0  Person
objc[23504]: [0x10080db68]       0x100750e10  Person
objc[23504]: [0x10080db70]       0x100750e30  Person
objc[23504]: [0x10080db78]       0x100750e50  Person
objc[23504]: [0x10080db80]       0x100750e70  Person
objc[23504]: [0x10080db88]       0x100750e90  Person
objc[23504]: [0x10080db90]       0x100750eb0  Person
objc[23504]: [0x10080db98]       0x100750ed0  Person
objc[23504]: [0x10080dba0]       0x100750ef0  Person
objc[23504]: [0x10080dba8]       0x100750f10  Person
objc[23504]: [0x10080dbb0]       0x100750f30  Person
objc[23504]: [0x10080dbb8]       0x100750f50  Person
objc[23504]: [0x10080dbc0]       0x100750f70  Person
objc[23504]: [0x10080dbc8]       0x100750f90  Person
objc[23504]: [0x10080dbd0]       0x100750fb0  Person
objc[23504]: [0x10080dbd8]       0x100750fd0  Person
objc[23504]: [0x10080dbe0]       0x100750ff0  Person
objc[23504]: [0x10080dbe8]       0x100751010  Person
objc[23504]: [0x10080dbf0]       0x100751030  Person
objc[23504]: [0x10080dbf8]       0x100751050  Person
objc[23504]: [0x10080dc00]       0x100751070  Person
objc[23504]: [0x10080dc08]       0x100751090  Person
objc[23504]: [0x10080dc10]       0x1007510b0  Person
objc[23504]: [0x10080dc18]       0x1007510d0  Person
objc[23504]: [0x10080dc20]       0x1007510f0  Person
objc[23504]: [0x10080dc28]       0x100751110  Person
objc[23504]: [0x10080dc30]       0x100751130  Person
objc[23504]: [0x10080dc38]       0x100751150  Person
objc[23504]: [0x10080dc40]       0x100751170  Person
objc[23504]: [0x10080dc48]       0x100751190  Person
objc[23504]: [0x10080dc50]       0x1007511b0  Person
objc[23504]: [0x10080dc58]       0x1007511d0  Person
objc[23504]: [0x10080dc60]       0x1007511f0  Person
objc[23504]: [0x10080dc68]       0x100751210  Person
objc[23504]: [0x10080dc70]       0x100751230  Person
objc[23504]: [0x10080dc78]       0x100751250  Person
objc[23504]: [0x10080dc80]       0x100751270  Person
objc[23504]: [0x10080dc88]       0x100751290  Person
objc[23504]: [0x10080dc90]       0x1007512b0  Person
objc[23504]: [0x10080dc98]       0x1007512d0  Person
objc[23504]: [0x10080dca0]       0x1007512f0  Person
objc[23504]: [0x10080dca8]       0x100751310  Person
objc[23504]: [0x10080dcb0]       0x100751330  Person
objc[23504]: [0x10080dcb8]       0x100751350  Person
objc[23504]: [0x10080dcc0]       0x100751370  Person
objc[23504]: [0x10080dcc8]       0x100751390  Person
objc[23504]: [0x10080dcd0]       0x1007513b0  Person
objc[23504]: [0x10080dcd8]       0x1007513d0  Person
objc[23504]: [0x10080dce0]       0x1007513f0  Person
objc[23504]: [0x10080dce8]       0x100751410  Person
objc[23504]: [0x10080dcf0]       0x100751430  Person
objc[23504]: [0x10080dcf8]       0x100751450  Person
objc[23504]: [0x10080dd00]       0x100751470  Person
objc[23504]: [0x10080dd08]       0x100751490  Person
objc[23504]: [0x10080dd10]       0x1007514b0  Person
objc[23504]: [0x10080dd18]       0x1007514d0  Person
objc[23504]: [0x10080dd20]       0x1007514f0  Person
objc[23504]: [0x10080dd28]       0x100751510  Person
objc[23504]: [0x10080dd30]       0x100751530  Person
objc[23504]: [0x10080dd38]       0x100751550  Person
objc[23504]: [0x10080dd40]       0x100751570  Person
objc[23504]: [0x10080dd48]       0x100751590  Person
objc[23504]: [0x10080dd50]       0x1007515b0  Person
objc[23504]: [0x10080dd58]       0x1007515d0  Person
objc[23504]: [0x10080dd60]       0x1007515f0  Person
objc[23504]: [0x10080dd68]       0x100751610  Person
objc[23504]: [0x10080dd70]       0x100751630  Person
objc[23504]: [0x10080dd78]       0x100751650  Person
objc[23504]: [0x10080dd80]       0x100751670  Person
objc[23504]: [0x10080dd88]       0x100751690  Person
objc[23504]: [0x10080dd90]       0x1007516b0  Person
objc[23504]: [0x10080dd98]       0x1007516d0  Person
objc[23504]: [0x10080dda0]       0x1007516f0  Person
objc[23504]: [0x10080dda8]       0x100751710  Person
objc[23504]: [0x10080ddb0]       0x100751730  Person
objc[23504]: [0x10080ddb8]       0x100751750  Person
objc[23504]: [0x10080ddc0]       0x100751770  Person
objc[23504]: [0x10080ddc8]       0x100751790  Person
objc[23504]: [0x10080ddd0]       0x1007517b0  Person
objc[23504]: [0x10080ddd8]       0x1007517d0  Person
objc[23504]: [0x10080dde0]       0x1007517f0  Person
objc[23504]: [0x10080dde8]       0x100751810  Person
objc[23504]: [0x10080ddf0]       0x100751830  Person
objc[23504]: [0x10080ddf8]       0x100751850  Person
objc[23504]: [0x10080de00]       0x100751870  Person
objc[23504]: [0x10080de08]       0x100751890  Person
objc[23504]: [0x10080de10]       0x1007518b0  Person
objc[23504]: [0x10080de18]       0x1007518d0  Person
objc[23504]: [0x10080de20]       0x1007518f0  Person
objc[23504]: [0x10080de28]       0x100751910  Person
objc[23504]: [0x10080de30]       0x100751930  Person
objc[23504]: [0x10080de38]       0x100751950  Person
objc[23504]: [0x10080de40]       0x100751970  Person
objc[23504]: [0x10080de48]       0x100751990  Person
objc[23504]: [0x10080de50]       0x1007519b0  Person
objc[23504]: [0x10080de58]       0x1007519d0  Person
objc[23504]: [0x10080de60]       0x1007519f0  Person
objc[23504]: [0x10080de68]       0x100751a10  Person
objc[23504]: [0x10080de70]       0x100751a30  Person
objc[23504]: [0x10080de78]       0x100751a50  Person
objc[23504]: [0x10080de80]       0x100751a70  Person
objc[23504]: [0x10080de88]       0x100751a90  Person
objc[23504]: [0x10080de90]       0x100751ab0  Person
objc[23504]: [0x10080de98]       0x100751ad0  Person
objc[23504]: [0x10080dea0]       0x100751af0  Person
objc[23504]: [0x10080dea8]       0x100751b10  Person
objc[23504]: [0x10080deb0]       0x100751b30  Person
objc[23504]: [0x10080deb8]       0x100751b50  Person
objc[23504]: [0x10080dec0]       0x100751b70  Person
objc[23504]: [0x10080dec8]       0x100751b90  Person
objc[23504]: [0x10080ded0]       0x100751bb0  Person
objc[23504]: [0x10080ded8]       0x100751bd0  Person
objc[23504]: [0x10080dee0]       0x100751bf0  Person
objc[23504]: [0x10080dee8]       0x100751c10  Person
objc[23504]: [0x10080def0]       0x100751c30  Person
objc[23504]: [0x10080def8]       0x100751c50  Person
objc[23504]: [0x10080df00]       0x100751c70  Person
objc[23504]: [0x10080df08]       0x100751c90  Person
objc[23504]: [0x10080df10]       0x100751cb0  Person
objc[23504]: [0x10080df18]       0x100751cd0  Person
objc[23504]: [0x10080df20]       0x100751cf0  Person
objc[23504]: [0x10080df28]       0x100751d10  Person
objc[23504]: [0x10080df30]       0x100751d30  Person
objc[23504]: [0x10080df38]       0x100751d50  Person
objc[23504]: [0x10080df40]       0x100751d70  Person
objc[23504]: [0x10080df48]       0x100751d90  Person
objc[23504]: [0x10080df50]       0x100751db0  Person
objc[23504]: [0x10080df58]       0x100751dd0  Person
objc[23504]: [0x10080df60]       0x100751df0  Person
objc[23504]: [0x10080df68]       0x100751e10  Person
objc[23504]: [0x10080df70]       0x100751e30  Person
objc[23504]: [0x10080df78]       0x100751e50  Person
objc[23504]: [0x10080df80]       0x100751e70  Person
objc[23504]: [0x10080df88]       0x100751e90  Person
objc[23504]: [0x10080df90]       0x100751eb0  Person
objc[23504]: [0x10080df98]       0x100751ed0  Person
objc[23504]: [0x10080dfa0]       0x100751ef0  Person
objc[23504]: [0x10080dfa8]       0x100751f10  Person
objc[23504]: [0x10080dfb0]       0x100751f30  Person
objc[23504]: [0x10080dfb8]       0x100751f50  Person
objc[23504]: [0x10080dfc0]       0x100751f70  Person
objc[23504]: [0x10080dfc8]       0x100751f90  Person
objc[23504]: [0x10080dfd0]       0x100751fb0  Person
objc[23504]: [0x10080dfd8]       0x100751fd0  Person
objc[23504]: [0x10080dfe0]       0x100751ff0  Person
objc[23504]: [0x10080dfe8]       0x100752010  Person
objc[23504]: [0x10080dff0]       0x100752030  Person
objc[23504]: [0x10080dff8]       0x100752050  Person
objc[23504]: [0x100817000]  ................  PAGE  (hot) 
objc[23504]: [0x100817038]       0x100752070  Person
objc[23504]: [0x100817040]       0x100752090  Person
objc[23504]: [0x100817048]       0x1007520b0  Person
objc[23504]: [0x100817050]       0x1007520d0  Person
objc[23504]: [0x100817058]       0x1007520f0  Person
objc[23504]: [0x100817060]       0x100752110  Person
objc[23504]: [0x100817068]       0x100752130  Person
objc[23504]: [0x100817070]       0x100752150  Person
objc[23504]: [0x100817078]       0x100752170  Person
objc[23504]: [0x100817080]       0x100752190  Person
objc[23504]: [0x100817088]       0x1007521b0  Person
objc[23504]: [0x100817090]       0x1007521d0  Person
objc[23504]: [0x100817098]       0x1007521f0  Person
objc[23504]: [0x1008170a0]       0x100752210  Person
objc[23504]: [0x1008170a8]       0x100752230  Person
objc[23504]: [0x1008170b0]       0x100752250  Person
objc[23504]: [0x1008170b8]       0x100752270  Person
objc[23504]: [0x1008170c0]       0x100752290  Person
objc[23504]: [0x1008170c8]       0x1007522b0  Person
objc[23504]: [0x1008170d0]       0x1007522d0  Person
objc[23504]: [0x1008170d8]       0x1007522f0  Person
objc[23504]: [0x1008170e0]       0x100752310  Person
objc[23504]: [0x1008170e8]       0x100752330  Person
objc[23504]: [0x1008170f0]       0x100752350  Person
objc[23504]: [0x1008170f8]       0x100752370  Person
objc[23504]: [0x100817100]       0x100752390  Person
objc[23504]: [0x100817108]       0x1007523b0  Person
objc[23504]: [0x100817110]       0x1007523d0  Person
objc[23504]: [0x100817118]       0x1007523f0  Person
objc[23504]: [0x100817120]       0x100752410  Person
objc[23504]: [0x100817128]       0x100752430  Person
objc[23504]: [0x100817130]       0x100752450  Person
objc[23504]: [0x100817138]       0x100752470  Person
objc[23504]: [0x100817140]       0x100752490  Person
objc[23504]: [0x100817148]       0x1007524b0  Person
objc[23504]: [0x100817150]       0x1007524d0  Person
objc[23504]: [0x100817158]       0x1007524f0  Person
objc[23504]: [0x100817160]       0x100752510  Person
objc[23504]: [0x100817168]       0x100752530  Person
objc[23504]: [0x100817170]       0x100752550  Person
objc[23504]: [0x100817178]       0x1007556d0  Person
objc[23504]: [0x100817180]       0x1007556f0  Person
objc[23504]: [0x100817188]       0x100755710  Person
objc[23504]: [0x100817190]       0x100755730  Person
objc[23504]: [0x100817198]       0x100755750  Person
objc[23504]: [0x1008171a0]       0x100755770  Person
objc[23504]: [0x1008171a8]       0x100755790  Person
objc[23504]: [0x1008171b0]       0x1007557b0  Person
objc[23504]: [0x1008171b8]       0x1007557d0  Person
objc[23504]: [0x1008171c0]       0x1007557f0  Person
objc[23504]: [0x1008171c8]       0x100755810  Person
objc[23504]: [0x1008171d0]       0x100755830  Person
objc[23504]: [0x1008171d8]       0x100755850  Person
objc[23504]: [0x1008171e0]       0x100755870  Person
objc[23504]: [0x1008171e8]       0x100755890  Person
objc[23504]: [0x1008171f0]       0x1007558b0  Person
objc[23504]: [0x1008171f8]       0x1007558d0  Person
objc[23504]: [0x100817200]       0x1007558f0  Person
objc[23504]: [0x100817208]       0x100755910  Person
objc[23504]: [0x100817210]       0x100755930  Person
objc[23504]: [0x100817218]       0x100755950  Person
objc[23504]: [0x100817220]       0x100755970  Person
objc[23504]: [0x100817228]       0x100755990  Person
objc[23504]: [0x100817230]       0x1007559b0  Person
objc[23504]: [0x100817238]       0x1007559d0  Person
objc[23504]: [0x100817240]       0x1007559f0  Person
objc[23504]: [0x100817248]       0x100755a10  Person
objc[23504]: [0x100817250]       0x100755a30  Person
objc[23504]: [0x100817258]       0x100755a50  Person
objc[23504]: [0x100817260]       0x100755a70  Person
objc[23504]: [0x100817268]       0x100755a90  Person
objc[23504]: [0x100817270]       0x100755ab0  Person
objc[23504]: [0x100817278]       0x100755ad0  Person
objc[23504]: [0x100817280]       0x100755af0  Person
objc[23504]: [0x100817288]       0x100755b10  Person
objc[23504]: [0x100817290]       0x100755b30  Person
objc[23504]: [0x100817298]       0x100755b50  Person
objc[23504]: [0x1008172a0]       0x100755b70  Person
objc[23504]: [0x1008172a8]       0x100755b90  Person
objc[23504]: [0x1008172b0]       0x100755bb0  Person
objc[23504]: [0x1008172b8]       0x100755bd0  Person
objc[23504]: [0x1008172c0]       0x100755bf0  Person
objc[23504]: [0x1008172c8]       0x100755c10  Person
objc[23504]: [0x1008172d0]       0x100755c30  Person
objc[23504]: [0x1008172d8]       0x100755c50  Person
objc[23504]: [0x1008172e0]       0x100755c70  Person
objc[23504]: [0x1008172e8]       0x100755c90  Person
objc[23504]: [0x1008172f0]       0x100755cb0  Person
objc[23504]: [0x1008172f8]       0x100755cd0  Person
objc[23504]: [0x100817300]       0x100755cf0  Person
objc[23504]: [0x100817308]       0x100755d10  Person
objc[23504]: [0x100817310]       0x100755d30  Person
objc[23504]: [0x100817318]       0x100755d50  Person
objc[23504]: [0x100817320]       0x100755d70  Person
objc[23504]: [0x100817328]       0x100755d90  Person
objc[23504]: [0x100817330]       0x100755db0  Person
objc[23504]: [0x100817338]       0x100755dd0  Person
objc[23504]: [0x100817340]       0x100755df0  Person
objc[23504]: [0x100817348]       0x100755e10  Person
objc[23504]: [0x100817350]  ################  POOL 0x100817350
objc[23504]: [0x100817358]       0x100755e30  Person
objc[23504]: ##############

能够看到当600*8=4800字节,所以一页肯定存不下,能够看到

................ PAGE (full) (cold) page 右边有个 cold、hot。cold 代表不是当时页,hot 代表当时页。

持续看看目标调用 autorelease 办法做了什么作业?

- (id)autorelease {
    return ((id)self)->rootAutorelease();
}
inline id objc_object::rootAutorelease() {
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
    return rootAutorelease2();
}
_attribute__((noinline,used)) id objc_object::rootAutorelease2() {
    assert(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj) {
    assert(obj);
    assert(!obj->isTaggedPointer());
    id *dest __unused = autoreleaseFast(obj);
    assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
    return obj;
}
static inline id *autoreleaseFast(id obj) {
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {
        return page->add(obj);
    } else if (page) {
        return autoreleaseFullPage(obj, page);
    } else {
        return autoreleaseNoPage(obj);
    }
}

检查 NSObject autorelease 办法调用链路能够看到最终仍是调用 AutoreleasePoolPage 的 add 办法(会判别有没有页、有没有满)

容器类会主动添加 AutoreleasePool

体系容器类,在运用 block 枚举器的时分,内部会主动创立 AutoreleasePool

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    @autoreleasepool {
        <#statements#>
    }
}];

所以,咱们老老实实写的 for、while 循环中需求手加局部 AutoreleasePool。引荐运用体系供给的容器类的 block 枚举器。

autorelease 目标什么时分调用 release 办法

每逢进行一次objc_autoreleasePoolPush调用时,runtime向当时的AutoreleasePoolPage中add进一个岗兵目标,值为0(也便是个nil),那么这一个page就变成了下面的姿态:

剖析 iOS 内存原理

objc_autoreleasePoolPush的回来值正是这个岗兵目标的地址,被objc_autoreleasePoolPop(岗兵目标)作为入参,于是:

  1. 依据传入的岗兵目标地址找到岗兵目标地点的page
  2. 在当时page中,将晚于岗兵目标刺进的一切autorelease目标都发送一次- release音讯,并向回移动next指针到正确位置
  3. 补充2:从最新加入的目标一向向前整理,能够向前跨越若干个page,直到岗兵地点的page

其次,AutoreleasePool 和 RunLoop 的也有联系

iOS 在主线程的 Runloop 中注册了2个 Observer

  • 第1个 Observer 监听了 kCFRunLoopEntry 工作,会调用objc_autoreleasePoolPush()

  • 第2个 Observer 监听了 kCFRunLoopBeforeWaiting 工作,会调用objc_autoreleasePoolPop()objc_autoreleasePoolPush()。还监听了kCFRunLoopBeforeExit工作,会调用 objc_autoreleasePoolPop()

结合 RunLoop 运转图

剖析 iOS 内存原理

  • 01 通知 Observer 进入 Loop 会调用 objc_autoreleasePoolPush

  • 做一堆其他作业

  • 07 在即将休眠的时分先调用 objc_autoreleasePoolPop,再调用 objc_autoreleasePoolPush

  • 等待唤醒做一堆其他作业,回到第二步

  • 07 又开端休眠,先调用 objc_autoreleasePoolPop,再调用 objc_autoreleasePoolPush

  • 11 没使命即将休眠,调用 objc_autoreleasePoolPop

能够看到 objc_autoreleasePoolPush、objc_autoreleasePoolPop 成对调用,贯穿 RunLoop

内存问题典型 case

OC 中有没有不对内存进行强持有的调集类型?

NSHashMap、NSMapTable 都能够描述 key、value 的内存润饰。

数组有 NSPointerArray 内部持有的是目标的指针,并非直接保存目标。不过 oc 转指针需求加 (__bridge void*) 进行润饰。NSPointerArray 的结构办法中能够经过 NSPointerFunctionsOptions 来声明内存的操控。

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p1 = [[Person alloc] init];
    Person *p2 = [[Person alloc] init];
    Person *p3 = [[Person alloc] init];
    NSPointerArray *arrays = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory];
//    NSMutableArray *array = [NSMutableArray array];
//    [array addObject:p1];
//    [array addObject:p2];
//    [array addObject:p3];
    [arrays addPointer:(__bridge void *)p1];
    [arrays addPointer:(__bridge void *)p2];
    [arrays addPointer:(__bridge void *)p3];
    p1 = nil;
    p2 = nil;
    // 断点设置到 NSLog,能够看到 Person 马上开释了
    NSLog(@"%@", arrays);
}
2022-05-24 21:57:27.071793+0800 TTTTW[63427:2087468] -[Person dealloc]
2022-05-24 21:57:27.071916+0800 TTTTW[63427:2087468] -[Person dealloc]
(lldb) 

NSError 内存走漏的 case

同事问了一个问题,下面的代码存在什么问题?

据说是 Zoom 这个公司的面试题,看了下其实便是考察 NSError 有没有踩过坑。怎样理解呢

- (BOOL)isZoomUserWithUserID:(NSInteger)userID error:(NSError **)error
{
    @autoreleasepool {
        NSString *errorMessage = [[NSString alloc] initWithFormat:@"the user is not zoom user"];
        if (userID == 100) {
            *error = [NSError errorWithDomain:@"com.test" code:userID userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
            return NO;
        }
    }
    return YES;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self test];
}
- (void)test {
    for (NSInteger index = 0; index <= 100; index++) {
        NSString *str;
        str = [NSString stringWithFormat:@"welcome to zoom:%ld", index];
        str = [str stringByAppendingString:@" user"];
        NSError *error = NULL;
        if ([self isZoomUserWithUserID:index error:&error]) {
            NSLog(@"%@", str);
        } else {
            NSLog(@"%@", error);
        }
    }
}

这段代码运转会 crash,信息如下

剖析 iOS 内存原理

原因是 NSError 结构办法内部会加 autorelease。源码如下

#define    AUTORELEASE(object)    [(id)(object) autorelease]
+ (id) errorWithDomain: (NSErrorDomain)aDomain
          code: (NSInteger)aCode
          userInfo: (NSDictionary*)aDictionary
{
  NSError    *e = [self allocWithZone: NSDefaultMallocZone()];
  e = [e initWithDomain: aDomain code: aCode userInfo: aDictionary];
  return AUTORELEASE(e);
}

MRC 下的 [(id)(object) autorelease] 等价于 ARC 下的 id __autoreleasing obj

所以这个问题的实质便是 autoreleasepool__autoreleasing 的问题

__autoreleasingis used to denote arguments that are passed by reference (id *) and are autoreleased on return.

__autoreleasing 润饰的变量会被添加到当时的 autoreleasepool 中。

办法的 Out Parameters 参数会主动添加 __autoreleasing 特点。当办法参数里边有 Out Parameters 参数时,便是有指针的指针类型时,编译器会主动为参数加上__autoreleasing 特点。改如下

- (BOOL)isZoomUserWithUserID:(NSInteger)userID error:(NSError **)error
{
    NSError *temp;
    @autoreleasepool {
        NSString *errorMessage = [[NSString alloc] initWithFormat:@"the user is not zoom user"];
        if (userID == 100) {
            temp = [NSError errorWithDomain:@"com.test" code:userID userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
        }
    }
    *error = temp;
    return YES;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self test];
}
- (void)test {
    for (NSInteger index = 0; index <= 100; index++) {
        NSString *str;
        str = [NSString stringWithFormat:@"welcome to zoom:%ld", index];
        str = [str stringByAppendingString:@" user"];
        NSError * __autoreleasing error = NULL;
        if ([self isZoomUserWithUserID:index error:&error]) {
            NSLog(@"%@", str);
        } else {
            NSLog(@"%@", error);
        }
    }
}

我写了个僵尸目标检测东西,作用如下

剖析 iOS 内存原理

能够定位僵尸目标,并且打印出详细仓库,并模拟体系行为调用 abort 。对监控原理和东西完成感兴趣的能够检查这里带你打造一套 APM 监控体系-内存监控之野指针/内存走漏监控

Demo 这里

内存是连续的吗?

应用发动后,Mach-O 文件是分段载入内存的。咱们运用的内存都是虚拟内存,经过内存映射表来做。

每个进程在创立加载时,会被分配一个大小大约为1~2倍实在地内存的连续虚拟地址空间,让当时软件以为自己具有一块很大内存空间。实际上是把磁盘的一小部分作为设想内存来运用。

CPU 不直接和物理内存打交道,而是经过 MMU(Memory Manage Unit,内存办理单元),MMU 是一种硬件电路,速度很快,主要作业是内存办理,地址转化是功用之一。

每个进程都会有自己的页表 Page Table ,页表存储了进程中虚拟地址到物理地址的映射联系,所以就相当于地图。MMU 收到 CPU 的虚拟地址之后就开端查询页表,确认是否存在映射以及读写权限是否正常。

iOS 程序在进行加载时,会依据一 page 大小16kb 将程序分割为多页,发动时部分的页加载进实在内存,部分页还在磁盘中,中心的调度记录在一张内存映射表(Page Table),这个表用来调度磁盘和内存两者之间的数据交换。

如上图,App 运转时履行某个使命时,会先拜访虚拟页表,假如页表的标记为1,则阐明该页面数据现已存在于内存中,能够直接拜访。假如页表为0,则阐明数据未在物理内存中,这时分体系会阻塞进程,叫做缺页中止(page fault),进程会从用户态切换到内核态,并将缺页中止交给内核的 page Fault Handler 处理。等将对应的 page 从磁盘加载到内存之后再进行拜访,这个进程叫做 page in。

由于磁盘拜访速度较慢,所以 page in 比较耗时,而且 iOS 不仅仅是将数据加载到内存中,还要对这页做 Code Sign 签名认证,所以 iOS 耗时更长

Tips:Code Sign 加密哈希并不少针对于整个文件,而是针对于每一个 Page 的,确保了在 dyld 进行加载的时分,能够对每一个 page 进行独立验证。

比及程序运转时用到了才去内存中寻找虚拟地址对应的页帧,找不到才进行分配,这便是内存的慵懒(延时)分配机制。

检测

依据 Instrucments 供给的东西的作业原理,写一个野指针探针东西去发现并定位问题。详细见野指针监控东西