继续上一篇根究下分类的加载流程。上一篇咱们根究类的加载其间methodizeClass办法有关于分类的加载。

1.分类加载推导

底层原理-15-类的加载(下)继续查看attachToClass办法,

底层原理-15-类的加载(下)发现增加分类的办法指针数组和数组指针的差异attachCategories,咱们用反推的办法判别哪里调用了它,发现有2处调用了,一处便是咱们上面attachToClass(第一处)中调用。

  • 再看load_测验工程师categories_nolock(第二处)调用

底层原理-15-类的加载(下)
指针式万用表怎样读数续查找load_categories_n指针olock,有2处Swift调用

底层原理-15-类的加载(下)第一处在loadAl苹果8pluslCategories调用。继续查找loadAllCategories得到

底层原理-15-类的加载(下)
查看第二处:
底层原理-15-类的加载(下)第二处在之前熟悉的read_images调用。其间只需当didInitialAttachCategories = true的时分才会进入。而didInitialAttachCategories默许苹果13false,在上面loadAswift世界结算体系llCategori测验郁闷程度的问卷es办法中会设置didInitialAttachCategoriestrue
依据剖析的流程能够得出增加分类的大约流程,如下苹果

底层原理-15-类的加载(下)

2. 分类的首要流程剖析。

咱们顺着代码流程剖析一下分类的加载

2.1 methodi指针的拼音zeClass简略剖析

static void methodizswiftlyeCla指针万用表的使用办法ss(Clas指针的拼音s cls, C苹果13lass previously)
{
......
// Attach categories.增加分苹果12
  if (previously) {
    if (isMeta) {
      //元类的话
      objc::unattachedCategories.attachT苹果8plusoClass(cls, previously,                       ATTACH_METACLASS);数组c言语
    } else {
      //类
      objc::unattachedCategories.attachToClass(cls, previously,
                          ATTACH_CLASS_AND_METACLASS);
    }
  }
  objc::unattachedCategories.attachToClas苹果xs(cls, cl指针式万用表图片s,
                      isMeta ? ATTACH_METACLASS : ATTACH_CLA测验手机是否被监控SS);
.....
}

previouslymethodizeClass办法带入进来的,它来自realizeCla指针万用表的使用办法ssWithoutSwift()大局查找

底层原理-15-类的加载(下)
发现调用处穿的都是nil,这个参数应该是苹果内部测验的自界说参swift代码是什么意思数。那么上面正常情况不会进入previously的判测验工程师别条件而是走下面的objc::unattachedCategories.attachToClas指针数学s(cls, cls,isMeta ? ATTACH_M指针数学ETACLASS : ATTACH_CLswift代码ASS);,previously便是cls。

2.2 attachToClass剖析

void attachToClass(Class cls, Class previ测验工程师ously, int flags)
  {
    runtimeLoc苹果13上市时刻k.assertLo测验纸怎样看是否怀孕cked();
    ASSERT((flags & ATT苹果ACH_CLASS) ||
       (flags & ATTACH_METACLASS) ||
       (flags & ATTACH_CLASS_ANswift怎样读D_METACLASS));
    aut苹果8pluso &map = get();
    auto it = map.find(previously);//查找分类previously指针数组是cls
 
    bool isMeta = cls->is数组公式MetaClass();
    const char *mangledName = cls->nonlazyMangledName();
    if (strcmp(mangledName, "LGPerson") == 0)
    {
      if (!isMeta) {
        printf("%s -LGPerson....n",__func__);
      }
    }
    if (it != map.end()) {
      //主类没有结束load,分类开始加载,主类会被逼结束
      category_list &amp苹果xr;list = it-&g指针数组t;second;
      if (flags & ATTACH_CLASS_AND_METACLASS) {
        //元类的进入
        int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
        attachCategories(cls, list.arra数组的界说y(), list.count(), otherFlag测验用例s | ATTACH_CLASS);数组词//实例办法
        attachCategories(cls->ISA(指针数组), list.array(), list.count(), otherFlags | ATTACH_METACLASS);//类办法
      } else {
        attachCategories(cls, list.array(测验用例), list.count(), flags);//不是元类
      }
      map.erase(it);
    }
  }

数组的长度要是将分类增加到主类,依据加载时机和类型。

2.3 attachCategories剖析

// Attach method测验工程师 lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loadswift世界结算体系ed and sorted by load order,
// oldest categories first.将类别中苹果手机的办法列表、特征和协议附加到类中。
static void
attachCategories(Class cls, cons测验纸怎样看是否怀孕t locstamped_category_t *cats_list, uint32_t cats_count,
        int flags)
{
  if (slowpath(PrintReplacedMethods)) {
    printReplacemen指针万用表的使用办法ts(cls, cats_list, cats_count);
  }
  if (slowpath(PrintConnecting)) {
    _objc_inform指针式万用表怎样读数("CLASS: attaching %d categories to%s class '%s'%s数组的长度",
          cats_count, (flags & AT数组和链表的差异TACH_EXISTINswift世界结算体系G) ? " existing" : "",测验智商
          cls->nameForLogginswifterg(), (fla测验智商gs & ATTACH_METACLASS) ? " (meta)" : "");
  }
  /*
  * Only a few cl苹果asses have more than 64 catego测验工程师ries during launchswiftcode代码查询.
  只需少数类在发布时具有逾越64个类别
  *测验你的自卑程度 This us苹果官网es a little stack, and avoid苹果12s malloc.
  *这使用了一个小库房,防止了malloc
  * Categories must be added in the proper order, which is back
  * to front. T数组排序o do that with the chunking, we iterate cats_list
  * from front to back, build up the local buffers backwards,
  * and call attachLists on the chunks. attach测验用例Lists prepends the
  * lists, so the final result is in th数组公式e expected order.
  */
  constexpr uint32_t ATTACH_BU苹果xFSIZ = 64;
  method_list_t  *mlists[ATTACH_BUFSIZ];
  property_list_t *prop测验郁闷程度的问卷lists[ATTACH_BUFSIZ];
  protocol_list_t *protoli测验网速sts[ATTACH_BUFSIZ];
  uint32_t mcoun数组的长度t = 0;
  uint32_t propcount = 0;
  uint32_t protocount = 0;
  bool fromBundle = Nswift代码O;
  bool isMeta = (flags & ATTACH_METACLASS);
  auto rwe = cls->data()->extAllocIfNeeded();//创立rwe假如没有创立的话,把主指针式万用表怎样读数类的data赋值给它
 
  const c数组指针har *mangledName = cls->nonlazyMangledName();指针
  if (strcmp(mangledName, "LGPerson") == 0)
  {
    if (!isMeta) {
      printf("attachCate测验你的自卑程度gories****LGPerson....n");
    }
  }
  for (uint32_t i = 0; i &l指针式万用表怎样读数t; cats_count; i++) {
    auto& entry = cats_list[i];
    method_list_t *mlist = entry.cat->met苹果手机怎样录屏hodsForMeta(isMeta);
    if (mlist) {测验郁闷症的20道题
      if (mcount == ATTACH_BUFSIZ) {
        //mcount = 64进入 mlists是一个二维数组
        prepareMethodLists(cls, mlists, mcount测验用例, NswiftlyO, fromBundle, __func__);//预备办法列表进行排序
        rwe->methods.attachLists(mlists, mcount);//赋值给rwe
        mcount = 0;
      }
      mlists[ATTACswiftcode代码查询H_BUFSIZ - ++mcount] = mlist;//把mlist赋值给mlists,mcount++。从后往前。
      fromBundle |= entry.hi->isBundle();
    }
    property_list_t *测验纸怎样看是否怀孕proplist =
      entry.cat->propertiesForMeta(isMetswiftera, entry.hi);
    if (proplist) {
      if (propcount == ATTACH_BUFSIZ) {
        rwe->properties.a数组c言语ttachLists(proplists, propcount);
        propcou苹果手机nt = 0;
      }
      proplists[ATTACH_BUFSIZ - ++propcount] = prop苹果xlist;
    }
    protocol_list_t *protolist = entry.测验怀孕的试纸图片一深一浅cat->protocolsFor苹果手机Meta(isMeta);
    if (protolist) {
      if (proswift怎样读tocount == ATTACH_BUFSIZ) {
        rwe-&g苹果手机怎样录屏t;protocols.attachLists(protol苹果8plusists, protocount);
        proto苹果手机怎样录屏count = 0;
      }
      protol苹果ists[ATTACH_BUFSIZ - ++protocount] = protolist;
    }
  }
  if (mc数组公式ount > 0) {
    prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
             NO, fromBundle, __func__);//排序 mlists是一个二维数组,+ ATTACH_BUFSIZ - mcount对mlist指针式万用表怎样读数s进行指针平移
    rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mc数组公式ount);
    if (flags & ATTACH_EXISTING) {
      flushCaches(cls, __func__, [](Class c){
        // constant caches have been dealt with in prepareMethodLists
        // if the class still is constant here, it's fine to keep
        retur指针式万用表图片n !c->cache.isConstantOptim指针万用表的使用办法izedCaswiftcode代码查询che();
      });
    }
  }
  rwe->pr苹果手机operties.attachLists(proplists + ATTAC苹果8plusH_BUFSIZ - propcount, propcount);
  rwe->protocols.苹果xa测验纸怎样看是否怀孕ttachLists(protolists + ATTACH_BUFS测验郁闷程度的问卷IZ - protocount, protocount);
}

首要界说一个最大容量64的二维数组mlists,数组的类型是method_list_t

底层原理-15-类的加载(下)
取出mlist数组赋值给mlists,从后往前进行赋值

底层原理-15-类的加载(下)

rwe 进行判别是否拓荒,没有拓荒的话进行拓荒extAllocIfNeeded

class_rw_ext_t *extAl苹果手机locIfNeeded() {
    auto v = get_ro_or_rwe();
    if (fastpath(v.is<class_rw_ext_t *>())) {//判别是指针数组否存在
      return v.get<class_rw_ext_t *>(&ro_or_rw_ext);//存在就获取
    } e苹果手机lse {
      return extAlloc(v.gswift体系et<const class_ro_t *>(&ro_o数组去重r_rw_ext));//不存在创立
    }
  }
class_rw_ext_t *
class_rw指针数组和数组指针的差异_t::extAllo数组c言语c(co数组nst class_ro_t *ro, bool deepCopy)
{
  runtimeLock.assertLocked();
  auto rwe = objc::zalloc<class_r测验智商w_数组指针ext_t>();//创立class_rw_ext_t类型的
  rwe->version = (ro->flags & RO_META) ? 7 : 0;
  method_list_t *list = ro->baseMethods();
  if (list) {
    if (deepCopy) list = list->duplicate()指针式万用表怎样读数;
    rwe->methods.att测验纸怎样看是否怀孕ac指针的拼音hLists(&list数组的界说, 1);
  }
  // See comments in objc_duplicateClass
  // property lists and protocol lists historically
  // have not been deep-co测验工程师pied
  //
  // This is probably wrong and ought to be指针数组 fix指针ed some day
  property_list_t *proplist = ro->basePrope测验郁闷程度的问卷rties;
  if (proplis指针c言语t) {
    rwe->properties.attachLists(&p指针的拼音roplist, 1);
  }
  protocol_list_t *protolist = ro->baseProtocols;
  if (protolist) {
    rwe->pr测验工程师otocols.attachLists(&prot测验你的自卑程度olist, 1);
  }
  set_ro_or_rwe(rwe, ro);
  ret苹果12urn rwe;
}

首要是判别是否存在rwe,不存在就创立,extAlloc进程是把本类的data测验纸怎样看是否怀孕指针的拼音存储到rwe中。

底层原理-15-类的加载(下)

2.4 attach苹果xrLists剖析

void attachLis数组指针ts(List* const * addedLists, uint32_tswift世界结算体系 addedCount) {
    if (addedCount == 0) return;
    if (hasArray()) {
      // many lists -> many lists 多维数组处理——>多维数组
      uint32_t oldCount = array()->count;//核算旧的数组巨细
      uint32_t newCount = oldCount + addedCount;//新数组巨细=旧的巨细+增加的
      array_t *new指针式万用表Array = (arr指针万用表的使用办法ay_t *)malloc(arr数组去重ay_t::b苹果13上市时刻yteSize(newCount));//依据新的所需巨细拓荒新数组
      newArray->count = newCount;//设置新数组巨细
      array()->count = newCount;//设置数组巨细为新的素组巨细
      for (int i = oldCount - 1; i >= 0; i--)
        newArray->li指针c言语sts[i + addedCount] = array()->lists[数组c言语i];//给新数组赋值从旧数组方位开始取,从后往前
      for (unsigned i = 0; i < addedC苹果xsount; i++)
        newArray->lists[i] = addedLists[i];//新增加的数组早年往后赋值
      free(array());
      setArray(newArray);//把二维数组从头测验怀孕的试纸图片一深一浅赋值。
      validate();
    }
    else if (!list && addedCount == 1) {
      // 0 lis指针c言语ts -> 1 list
      list = addedLists[0];//榜初度进来的时分直接取数组列表第一个元素,赋值给list,是一个一维数组
      validate();
    }
    else {
      // 1 list -> many lists 一个二维数组到多个二维swift世界结算体系数组
      Ptr<List> oldList = list;//取出之前的-维数组
      uint32_t oldCo苹果12unt = oldList ? 1 : 0;//运算符,判别是否有旧的数组存在,存在便是1,不存在是0
      uint32_t newCount = oldCount + addedCount;//新的二维数组巨细 = 增加的 + 之前的
      setArray((array_t *)malloc指针万用表的读法(array_t::byteSize(newCount)));//依据新的数组巨细拓荒新的空间
      array()->count = newCount;//设置数组巨细
      if (oldList) array()-&g数组排序t;lists[数组排序addedCount] = oldList;//存在之前的数组,把二维数组的结尾赋值就的一维数组
      for (unsigned i = 0; i &测验郁闷程度的问卷lt;指针式万用表 addedCount; i++)
        array()->lists[i] = addedLists[i];//遍历增加二维数组增加,从o方位开始swift怎样读赋值,增加的swiftly
      validate();
    }
  }

attachLists刺进表首要是增加二维数组,把老的二维数组变成新的。二维数组的类型是array_t

struct array_t {
    uint32_t count;//数组个数
    Ptr&测验智商lt;List> lists[0];//数组的0号方位
    static size_t byteSize(ui测验nt32_t count) {
      return sizeof(array_t) + count*sizeof(lists苹果手机怎样录屏[0]);
    }
    size_t byteSize测验郁闷程度的问卷() {
      return byteSize(count);//巨细
    }
  };
  1. many lists -> many lists

先核算出之前的二维数组巨细,新的数组巨细。依据新的巨细拓荒新的array_t数组,之后把老的array_t数组的值从后往前塞进新的array_t数组。
2. 0 lists -> 1 list
二维数组为空,增加的数量为1.榜初度进来指针式万用表怎样读数。addedLists只需一个值直接测验你的自卑程度0方位的值,赋值给list。
3. 1 list -> many lists
取出之前的二维数组只需一个值,苹果13之前测验智商看过array_t的数据格式,知道Ptr<List数组公式> lists[0]。核算出新增加的二维数组巨细指针,依据新的巨细创立新的array_t数组。先吧就的值赋值到新数组的终究一位,因为只需一个不必循环;之后循环从初始方位赋值swiftkey
流程如下:
底层原理-15-类的加载(下)

3. 主类和分类加载时机

咱们之前根究到类的加载分数组排序懒加载非懒加载,那么按照节省内存的情况,分类也应该相似。但是咱们剖析了分类加载依赖于类,swiftcode代码查询所以就有4种情况,咱们逐个剖析。
底层原理-15-类的加载(下)

3.1 主类和分类都结束load办法

咱们在attachCategories中添swifter加断swiftcode代码查询点,进入断点后,咱们打印bt信息。

底层原理-15-类的加载(下)
得出流程loa苹果官网d_images–>loadAllCategori指针式万用表怎样读数es–>load_cate数组公式gories_nolock–>attachCategories

3.2主类不结束+load,分类结束+load

在main函数前打个断点防止进入懒加载流程

底层原理-15-类的加载(下)
发现没有进入attachCategories,明明现已分类结束了+load,应该走非懒加载办法进入attachCategories但是却没有。那么究竟分类的信息增加到类没有?带着疑问我在

底层原理-15-类的加载(下)
阐明这个在结束类的时分也读取了分苹果13上市时刻类的数据。主类没有进行+load办法调用,指针万用表的读法也没有发送音讯进入慢速查找流程结束类。那么怎样进入re测验纸怎样看是否怀孕alizeClassswift怎样读WithoutSwift?上一步

底层原理-15-类的加载(下)
这段代码测验怀孕的试纸图片一深一浅是结束非慵懒类也便是+load办法结束的。是prepare_load指针式万用表怎样读数_methods办法回调。完苹果结分类+load办法时分会把主类也结束,读取data()包含了分类和主类的数据,而数据在编译的时分就供认了指针c言语。得出苹果13上市时刻流程 _read_images–>readClass–>realizeClassWithoutSwift

3.2.1 主类不结束+load,数组词多个分类,只需一个分类结束+load;

新建分类LGB,LGC,

底层原理-15-类的加载(下)

底层原理-15-类的加载(下)
不结束+load办法,继续上面剖析。

底层原理-15-类的加载(下)
效果和上面只需一个+load相同。流苹果13_read_image指针式万用表图片s–>readClass–>realizeClas数组公式sWithoutSwift

3.2.2 主类不结束+load,分类指针数组和数组指针的差异结束2个以上+load;

依据自界说断点可知。先进入

底层原理-15-类的加载(下)
之后进入load_swiftcode代码查询cate数组公式gories_nolock,加载分类。苹果13上市时刻

底层原理-15-类的加载(下)
之后进入prepare_load指针数学_methods

底层原理-15-类的加载(下)
查找分类中结束+load的分类列表,并结束主类realizeClassWitho指针c言语utSwift

底层原理-15-类的加载(下)
在主类结束好进入methodizeClass,继续下面的流程,打印库房信息

底层原理-15-类的加载(下)
流程如下:load_images–>loadAllCategories(加载分类)–>load_categories_nolock(处理分类数据)–>prepare_load_methods(预备)–>realizeClassWithoutSwift(结束类)–>methodizeClass–>attachToClass–>attachCategories(增加分类)

3.3 主类结束+load,分类不结束+load

分类都不结束+load方数组的界说法,只需主类结束+load。主类走咱们之前根究类数组初始化的时分结束类代码中的非懒加指针的拼音载代码块

底层原理-15-类的加载(下)
打印一指针式万用表图片下库房的信息,收拾下流程

底层原理-15-类的加载(下)
流程如下:map_images–>map_images_nolock–>_read_images数组指针>realizeClassWithoutSwift–>methodswiftkeyizeClass在编译的时分供认了类和分类的data

3.4 主类和分数组初始化类不结束+l测验郁闷症的20道题oad

底层原理-15-类的加载(下)
当榜初度调用类发送音讯的时分,不论调用的是类的办法还是分类的办法,走类的懒加载流程。此时流程:lo指针数学okUpImpOrForwaswift怎样读rd –>realizeClassMaybeSwiftMaybeRelo指针式万用表ck–>realizeClassWithoutSwift

底层原理-15-类的加载(下)

4.总结

  • 分类加载流程剖析能够经过反推的办法,去查指针数学找它的流程。
  • 分类增加到主类的时分会把数据写入rwe,判别是否现已存在rwe,不存在就创立一个一同把主类的数据也写入。分类的数据写入经过attachList写入特征,办法列表,协议等。写入进程是依据增加表的个数加之前增加指针万用表的使用办法的个数拓荒新的数组,之前的数组Swift从后往前刺进新的数组,行将增加的从前往后增加到新的数组。
  • 分类加载的时机取决于主类是否是懒加载和分类自身是否是懒加载。文中3数组的长度.1都对错懒加载,会经过+load去结束loadA数组c言语llCategories;3.2分为2种情况,分类结束一个+load的时分,主类会指针万用表的使用办法逼结束,直接读取编译时供认的data(),包含了主类和分类的数据;另一种情况是结束2个以上分类+load,它分2步操作,一个是loadAllCategories,另一步是结束主类增加分类attachCategories3.3只结束主类+load,分类不结束。走主类非懒加载流程。一swiftkey起编译时的data()包含了分类的数据。3.4swift代码是什么意思是懒加载的时分,数组的界说榜初度发送音讯的时分,走类的懒加流程。读取daa()