コアテンプレート構造の基礎となるios-list_array_tt&entsize_list_tt

objcの基本的な原則を探求する過程で、テンプレートlist_array_ttに遭遇することがよくあります。

この構造は、クラスの読み込み、メソッドルックアップ、ハッシュテーブル、同期ロックなどで発生します。C++のテンプレート関数は非常に強力ですが、コードは非常に厄介に見えます。

実際、それほど悪くはありません。この構造を理解することを試みることができます。これは、iOSの基盤となるソースコードを探索するのに非常に役立ちます。テンプレートの観点から考慮しない場合は、ソースを分析する過程でコードでは、多くのロジックが無関心になり、基礎となる理解が停止します

プレビューlist_array_tt

image.png

list_array_ttは、ランタイムヘッダーファイルに存在するテンプレートであり、ランタイムAPIにとっての重要性を示しています。

テンプレートの命名の観点から、最も単純で最も下品な理解であるList Elementは、論理的にはコンテナーListとelement Elementの間の関係ですが、単純な線形関係ではありません。

image.png

list_array_ttバーズアイビュー構造

  • array_t構造

  • イテレータ

  • リストポインタですが、特別なことに、配列を表すことができる追加の関数があります

list_array_ttキーメソッドの終了

  • hasArray()には配列関数がありますか

  • array()は配列コンテナを返します

  • setArray(array_t * array)

  • count()要素の数

  • begin()end()...イテレータ反復関数

  • countLists()配列配列の要素リストの数は、リストの数です。

  • beginLists()endLists()最初のリストと最後のリスト

  • attachLists(List * const * usedLists、uint32_t usedCount)配列追加リストロジック很重要

プレビューentsize_list_tt

image.png

entsize_list_ttもテンプレートであり、ランタイムヘッダーファイルにも存在します

image.png

entsize_list_ttバーズアイビュー構造

  • entsizeAndFlags要素サイズの記録に加えて、追加のビットにはフラグ情報が含まれています

  • countは、テンプレート要素の数として下品に理解できます

  • イテレータもあります

entsize_list_tt 关键方法整理

  • entsize() 元素大小

  • get(uint32_t i) 获取元素

  • byteSize() 模板List字节大小

  • begin() end() 迭代器迭代功能

此处发现有个迭代器,list_array_tt也有个迭代器,会感觉烦乱,后面我会拆解

entsize_list_tt 迭代分析

迭代器iterator是个内部结构体 内部结构

  • entsize 迭代元素大小

  • index 索引 主要是重载运算符号 中使用

  • element 迭代元素指针

迭代器iterator的迭代功能其实很简单,主要通过运算符号重载实现迭代器的 ++ -- *取元素 ->取元素指针 ==判断元素相等 != > < 比较大小

operator 重载符号 是c++的重载运算符的语法,就是把运算符重载为迭代器的函数,运算符当作函数,通过传惨调用函数 实现迭代过程中的运算逻辑

image.png

自增1 自减1 都是通过迭代器当前元素指针偏移 指针节点个数来实现的,可以对照链表偏移节点那样去理解 但你再仔细看下,这是内存偏移

可以简单猜测一下 此处的迭代器 在 list_array_tt中也会使用到,先不用考虑list_array_tt中的迭代器结构,从鸟瞰代码预览中,大致已经知道 list_array_tt 是个两层嵌套结构

list_array_tt 双层嵌套结构

  • 外层可以类似看作是个数组结构,数组元素为模板List

  • 模板List是内层结构,作为 list_array_tt的一个元素,而前面提到的迭代器主要是为了迭代List模板容器中的元素而创建的

  • 对于 list_array_tt中的迭代器,相信最终调用的还是 此处entsize_list_tt中的迭代器

回到 list_array_tt

list_array_tt 中的迭代器分析

image.png

可能会有人觉得这个List::iterator 理解起来有点困难

其实这个List就是模板 entsize_list_tt ,这么说可能还是有点迷糊 举个实例或许比较容易理解一些

image.png

objc类的底层结构 method_array_t property_array_t protocol_array_t 都是继承自list_array_t

而且 这几个数据结构 都采用了一样的模板结构,不管是叫 method_list_t, property_list_t, 还是 protocol_list_t

image.png

在 list_array_tt 中 都对应了 模板List,元素都是Element

我们再分别看下 method_list_t, property_list_t 这几个模板具体定义

  • method_list_t

image.png

  • property_list_t

image.png

image.png

不管 method_list_t, property_list_t 的名字怎么起,最终都是继承自 entsize_list_tt模板结构而来, 就是模板容器List

上面的例子稍微总结下

  • method_array_t properties_array_t protocol_array_t 或者其他什么结构,都套用一个通用嵌套模板容器

  • 如果你的结构复杂些,就用双层嵌套结构

  • 而结构如果简单,就复用这个双层嵌套,当作单层List容器使用,只不过外层array只有一个元素而已

  • 你可以试想一下ios中的分类,如果存在多个分类,均扩展同名方法,你会怎么设计呢?

  • 再者如果设计一个哈希结构,你又会怎么设计,我们都知道哈嘻是个函数,不同的参数,得到的哈嘻位置可能会重复,你又会怎么设计这个哈嘻结构呢?

试着多想想,发散开了想,既然runtime中这么多的复用这个结构,自然这个结构很重要了,其实不只iOS底层了,其他很多语言都有这样的类似结构

c++本身就是跨平台的,而你也许听过了解c++,就必须深入了解它的模板机制

好了 回归到上面的 list_array_tt 迭代器,在这个迭代器里,出现了 List::iterator

  • 仔细看前面的例子,看 method_list_t, property_list_t 继承关系截图 如 method_list_t 与 <> 中的 模板名字method_list_t 是一样的

  • 既然 method_list_t 继承自 entsize_list_tt, 那么 method_list_t中的迭代器就是 method_list_t::iterator

  • 这是具体实例,再还原为抽象模板就是 List::iterator

  • 如此 list_array_tt 迭代器就是调用了 entsize_list_tt中的迭代器而已

image.png

这是 list_array_tt 中的迭代器

Ptr就是模板List的指针封装而已,就是List的begin end函数,也就是 entsize_list_tt中的迭代器 begin end函数

至此 前面的关于 外层迭代器调用 内层迭代器的猜想得证

list_array_tt 中的迭代器操作理解

  • 重载运算符++ 外层元素也就当前访问从数组中的当前元素List ,平移到下一个元素List上

    然后迭代器中的m指向目标List的第一个元素,mEnd指向目标List的最后一个元素

  • 重载运算符 *取出 当前访问的目标List 中正指向的元素 也就是m指向的元素

list_array_tt 中成员array_t 与 list理解

  • 如果 list_array_tt 中设置了array,那么当前生效的就是 array_t结构,使用时遵循两层嵌套结构

    每次访问一定需要知道 begin end,就是 先找到 array_t, 初始取到 lists(array结构中的首地址 也就是第0个元素的地址)

    每次遍历 如 ++ 或 -- 也就是 lists移动到 后一个List 或 前一个List,同时 m指向目标List的第一个元素 mEnd指向目标List的最后一个元素

  • 而如果 list_array_tt 中未设置 array,那么就是单层结构

    使用list这个成员 就不存在 array的目标List平移查找逻辑了

  • 二重にネストされた構造があるかどうかは、arrayAndFlag&1によって判断されます。

  • 二重にネストされた構造体配列getarrayAndFlag&〜1 get

list_array_tt-attachListsプロセス分析

これはこの記事の中心的な内容ですが、前の予兆がなければ、この記事を説明することはできません

なぜそれが重要なのですか?iosクラスのロードプロセスを理解したい場合は、このattachListsプロセスをバイパスできないようにする必要があります。そうしないと、rweと分類のロードロジックを理解できなくなり、この認識は常に折りたたまれた状態で

void attachLists(List * const * usedLists、uint32_t usedCount)

  • パラメータaddedListsはテンプレートリストコレクションであり、複数のテンプレートリストがあることに注意してください

  • まず、二重にネストされた構造があるかどうかを判断します

  • 2層構造がない場合、つまり現在配列がなく、addedListsに要素テンプレートリストが1つしかない場合

    次に、配列なしの状態を維持し続け、addedLists内の唯一の要素List(addedLists [0])のみを取り出し、それをlistに割り当てます。

  • 2層構造がないが、addedListsに複数の要素テンプレートリストがある場合

    配列配列を開きます。リストがあるかどうかに応じて、配列要素の数がaddedCountまたはaddedCount+1になります。

    attachListsの要素は、順番に0番目の位置から始まり、順番に配列に格納されます。

    元のリストが存在する場合は、元のリストを配列の最後の位置に配置します

  • 二重にネストされた構造、つまり既存の配列がある場合

    新しい配列を作成しますnewArray、配列内の要素の数が追加されますCount+元の配列内のリストの数配列カウント

    配列内の古い要素は、newArrayの最後のカウント位置に順番に格納されます。

    usedListsの要素は、newArrayの最初のaddedCount位置に順番に格納されます。

    古いアレイスペースを解放します

おすすめ

転載: juejin.im/post/7116983009701527588