您当前的位置: 首页 > 语文作业 > 08--内存管理 04--弱原理
08--内存管理 04--弱原理
发布时间:2023-10-06 17:31
  1. 08--内存管理 04--弱原理
  2. MRC、ARC 内存管理机制
  3. iOS/OS X 内存管理(一):​​基本概念与原理
  4. iOS内存管理
  5. 内存管理原理
  6. 08--内存管理--大华自动释放池
  7. 内存管理分析
  8. ios内存管理机制2016
  9. 2019-01-14 day16!! !python内存管理
  10. OC内存管理
总有机碳

弱分析思路:汇编+源码

  • 开放编译
  • 定位objc_initWeak
  • 全局搜索objc_initWeak,找到实现的地方
objc_initWeak
  • 找到关键功能storeWeak
存储弱
  • 功能要点分析storeWeak核心实现
关键流程
  • 找到关键函数weak_register_no_lock
弱寄存器无锁

弱修改原理:流程

变量描述

过程中你总会遇到以下变数

weak_tableweak_table_t类型,全局弱引用表
entryweak_entry_t类型,弱变量
的存储实体 referentobjc_object *类型,对对象的引用,这里表示弱引用对象指针
referrerobjc_object **类型,引用的地址,这里表示弱引用对象指针的指针

输入功能

弱源代码流程图

objc_initWeak
商店弱

1。类初始化

条件:如果该类正在初始化,则应先初始化class_initialize

2。 weak_unregister_no_lock

条件:如果有旧值,先清理weak_unregister_no_lock

  1. 非空判断,if (!referent)返回;

  2. weak_table weak_entry_t—— 获取

  3. 弱引用入口合集

  4. 如果entry存在,删除旧的弱引用:remove_referrer(entry,referrer);

  5. 如果 entry 为空,则 weak_table 将其从 entry 中删除

    如果(空)
        weak_entry_remove(weak_table, 条目);
    

3。 weak_register_no_lock

条件:如果有新值,则存储weak_register_no_lock

  1. 如果在weak_table中找到弱引用对象referent,则弱引用集合进入

    if ((条目=weak_entry_for_referent(weak_table,referent)))
    
  • append_referrer

    • 如果满足以下条件,请先扩展,然后插入
    if (条目->num_refs >= TABLE_SIZE(条目) * 3/4)
        Growth_refs_and_insert(条目,new_referrer);
    
    • 保存新弱引用指针的地址
    //保存新弱引用指针的地址
    weak_referrer_t &ref = 条目->referrers[索引];
    ref = new_referrer;
    // 采集数量+1
    条目->num_refs++;
    
  1. 如果不存在弱引用集合进入

    • 创建一个新的弱引用集合enterweak_entry_t new_entry(referent,referrer);
      功能1:创建新的弱引用集合enter
      功能2:保存这个弱引用指针的地址

    • 如果weak_table需要扩容,则扩容:weak_grow_maybe(weak_table);

    • 将这个新的弱引用集合插入到weak_table中:
      weak_entry_insert(weak_table, &new_entry);

弱释放原理:流程

在ARC机制下,当一个对象的引用计数为0时,会向其发送释放消息。在执行release的时候,会执行dealloc方法。 dealloc方法中,weak_clear_no_lock( &table.weak_table, (id)this);清除弱引用的方法

if (*引用者 == 引用者) {
    *引荐来源=零;
}

通过引用的地址将引用(弱变量)清空

1。 dealloc过程

_objc_rootDealloc(self);

--> obj->rootDealloc();

----> object_dispose((id)this);

------> objc_destructInstance(obj);

--------> obj->clearDealaving();

---------->clearDealaving_slow();

------------> if (isa.weakly_referenced) {------------>weak_clear_no_lock(&table.weak_table, (id)this);
----------> }

2。 weak_clear_no_lock进程

参数
weak_table:全局弱引用表
referent:待释放的弱引用对象

  1. 获取弱引用
    的集合条目 weak_entry_t *entry=weak_entry_for_referent(weak_table,referent);
  2. 遍历集合enter中的弱引用指针,并将其置空
    *推荐人 = nil;
  3. 已删除条目集合
    weak_entry_remove(weak_table, 条目);

3。 weak_entry_remove过程

  1. 发布参赛作品集参考
    免费(条目->推荐人);
  2. weak_table 计数减少 1
    weak_table->num_entries;
  3. 重置weak_table
    的大小 weak_compact_maybe(weak_table);

弱修改原理:记忆表

上面描述了弱修改弱释放的过程,但是没有分析它们在内存中是如何存在的。下面详细讲解

1。什么是弱变量?

先看一段代码

LG老师*老师= [LG老师分配];__weak id wobjc1 = 老师;

__weak id wobjc2 = 老师;

__strong id soobjc1 = wobjc1;

__strong id soobjc2 = wobjc2;

NSLog(@"--%@, --%@, --%@, --%@,", wobjc1, wobjc2, sobjc1, sobjc2);

输出结果

--,--,--,--,

用weak修饰的变量就是弱变量。代码中使用了wobjc1wobjc2weak修改了的变量,sobjc1 sobjc2 使用 strong来修改的变量,但是我们从输出中可以看到它们的值都是一样的,那么weakstrong有什么区别?

弱变量和强变量的存储方式不同。存储方法稍后介绍。

这四个变量的值都是老师的。 Teacher表示的是对象的首地址,也就是输出结果中的地址0x1012525f0。如果忽略__weak__strong,上面四个表达式只是赋值运算符,将右边的值赋给左边。由于这两个修饰符的存在,它们的含义不同,它们在内存中的存储方式也发生了变化。

参考、对象、类型

还是上面的代码。

参考对象类型
  • 对象:alloc分配的一块内存
  • 引用:老师 - 强引用,wobjc1wobjc2 - 弱引用,sobjc1 sobjc2 - 强引用
  • 类型:id、LGTeacher,都是类型。在探索类的结构时,我们可以直接获取对象的某个属性的地址。但是在开发中我们并不能直接通过这个地址来修改属性的值。我们只能根据类型来使用变量的setter方法。设置属性的值,这是对内存的一种保护机制。

先介绍一下手表的原理吧

3。弱修改原理:内存表

例如:当程序执行下面这行代码时,会访问哪些内存相关的操作,如下流程图所示。

__weak idweakRef = objc

referent:weakRef,表示这个弱引用变量
referrer:&weakRef,表示这个弱引用变量的地址

弱记忆流程图

变量

weak_tableweak_table_t类型,全局弱引用表
entryweak_entry_t类型,弱变量
的存储实体 referentobjc_object *类型,对对象的引用,这里表示弱引用对象指针
referrerobjc_object **类型,引用的地址,这里表示弱引用对象指针的指针

一般流程

  1. 首先从SideTable中找到weakTable;
  2. 然后从weakTable中查找entry,如果没有则创建一个新的;
  3. 将弱引用保存到entry中;

lldb探索

  1. 第一次进来:weak_table为空
图像
  1. 执行else过程后,weak_table中出现一条条目
图像
  1. 第二次进来:条目中有数值

第一笔存款成功。因为是第二次进来了,如果没有找到第一次存的东西我肯定会觉得不舒服

  • 输出条目信息:p *entry

    入口
  • 输出referent的键值weak_entry_t,对象的首地址:p undisguise(18446744069395690144)

    所指对象
  • 在引用者中输出weak_referrer_t:p $16.referrersp $16.inline_referrers[0]。注释说明这个值经过了一些操作,比如指针对齐,所以找不到次地址和主地址。这个问题稍后会探讨。

    弱引用者_t
  1. 插入后,条目中有两个值。

    图像

通过以上一波操作,弱变量已经从不可见的状态中显露出来,呈现在我们的眼前。它不再只是头脑中的幻觉。

为什么条目中会存储referrer(引用地址)?

大家应该都听过一句话——弱变量会自动被设置为nil。原理就在这个referrer

通过引用的地址将引用(弱变量)清空

4。弱引用表:weak_table_t

1。 weak_table_t结构

/**
 * 全局弱引用表。将对象 ID 存储为键,
 * 和weak_entry_t 结构作为它们的值。
 */
结构体weak_table_t {
    weak_entry_t *weak_entries;
    size_t num_entries;
    uintptr_t 掩码;
    uintptr_t max_hash_displacement;
};

评论里说得很清楚了

  • 这是一个全局弱引用表
  • key:对象id作为key
  • 值:weak_entry_t结构作为值

2。 weak_entry_t

的搜索过程
静态weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    size_t begin = hash_pointer(referent) &weak_table->mask;
    size_t索引=开始;
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent !=referent) {
        索引=(索引+1)&weak_table->掩码;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        散列位移++;
        if (hash_displacement >weak_table->max_hash_displacement) {
            返回零;
        }
    }
    
    返回&weak_table->weak_entries[索引];
}

对于哈希表来说,重点就是下标的计算,计算weak_table中下标的代码:

size_t begin = hash_pointer(referent) &weak_table->mask;
size_t索引=开始;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent !=referent) {
    索引=(索引+1)&weak_table->掩码;
    if (index == begin) bad_weak_table(weak_table->weak_entries);散列位移++;
    if (hash_displacement >weak_table->max_hash_displacement) {
        返回零;
    }
}

计算完下标后,返回weak_entry_t的指针,以便外部对其进行修改,进行插入或删除操作

return &weak_table->weak_entries[index];

5。弱实体表:weak_entry_t

1。 weak_entry_t源代码

结构weak_entry_t {
    DisguishedPtr 指称;
    联盟{
        结构体{
            weak_referrer_t *引用者;
            uintptr_t out_of_line_ness : 2;
            uintptr_t num_refs : PTR_MINUS_2;
            uintptr_t 掩码;
            uintptr_t max_hash_displacement;
        };
        结构体{
            // out_of_line_ness 字段是 inline_referrers[1] 的低位
            weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    布尔 out_of_line() {
        返回(out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }

    weak_entry_t& 运算符=(constweak_entry_t& other) {memcpy(这个, &other, sizeof(other));
        返回*这个;
    }

    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        :参考对象(新参考对象)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

可以看到这里做的操作并不多,主要是两个属性,

2。属性所指

这是一个将对象地址保存为哈希表的键值的属性。

DisguishedPtr 指称;

3。属性引用者

这是联合类型,存储引用地址(辅助指针)。

联盟{
    结构体{
        weak_referrer_t *引用者;
        uintptr_t out_of_line_ness : 2;
        uintptr_t num_refs : PTR_MINUS_2;
        uintptr_t 掩码;
        uintptr_t max_hash_displacement;
    };
    结构体{
        // out_of_line_ness 字段是 inline_referrers[1] 的低位
        weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
    };
};

4。 weak_referrer_t:DisguishedPtr 类

  • weak_referrer_t 类型

    typedef DisguishedPtrweak_referrer_t;
    
  • DisguishedPtr 类:提供两个静态方法,分别是:

    • 将地址反转成整数存储,可以理解为encode
    • 将存储的整数类型转换为指针并返回,可以理解为decode
    模板 // 通用
    类 DisguishedPtr {
        uintptr_t 值;
        // 编码
        静态 uintptr_t 伪装(T* ptr) {
            返回 -(uintptr_t)ptr;
        }
        //解码
        静态 T* undisguise(uintptr_t val) {
            返回(T*)-val;
        }
        ……
    }
    

    其实上面输出referent属性时,就用到了这个decode操作,

    所指对象

    苹果这样做的目的是为了防止泄密者跟踪这些弱地址。目前还没有找到令人信服的理由,以后遇到了再补充。

5。 append_referrer 方法

size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t索引=开始;
size_t hash_displacement = 0;while (entry->referrers[index] != nil) {
    散列位移++;
    索引 = (索引+1) & 条目->掩码;
    如果(索引==开始)bad_weak_table(条目);
}
if (hash_displacement > 条目->max_hash_displacement) {
    条目->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = 条目->referrers[索引];
ref = new_referrer;
条目->num_refs++;

这个方法和上面weakTable查找条目的方法一致:

  1. 计算索引索引
  2. 索引处插入weak_referrer_t

弱原理总结

  1. 理解引用、对象、类型等名词的含义;
  2. 了解弱存储的过程;
  3. 弄清楚弱释放过程;
  4. 弄清楚weak在内存中存储的程度以及它存储在内存中的位置;
  5. 弱实体存储弱变量的地址,以便在释放时清空弱变量;
  6. 弱实体存储弱变量的地址时,进行一步取反操作,然后在读取时取反;
相关阅读