本文完整版共三篇:

  • 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 上)
  • 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 中)
  • 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 下)

2 类是怎么加载的

2.2 _dyld_objc_register_callbacks

map_images

_read_images

第三部分:读取类
///+ 读取类
for (EACH_HEADER) {
    ///+ 镜像已被彻底优化,无需读取类
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // Image is sufficiently optimized that we need not call readClass()
        continue;
    }
    ///+ 从 “__objc_classlist” 区中读取类引证
    classref_t const *classlist = _getObjc2ClassList(hi, &count);
    bool headerIsBundle = hi->isBundle();
    bool headerIsPreoptimized = hi->hasPreoptimizedClasses();  
    for (i = 0; i < count; i++) {
        ///+ 获取类地址
        Class cls = (Class)classlist[i];
        ///+ 读取该类
        Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);  
        if (newCls != cls && newCls) {
            // Class was moved but not deleted. Currently this occurs
            // only when the new class resolved a future class.
            // Non-lazily realize the class below.
            ///+ 类被移动了,但并未删去,仅产生一个信息解析 future 类时。
      ///+ 将该类参加到拖延辨认数组中
            resolvedFutureClasses = (Class *)
                realloc(resolvedFutureClasses,
                        (resolvedFutureClassCount+1) * sizeof(Class));
            resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
        }
    }
}

要点就在于 readClass 这个函数了,瞄一眼:

///+ 读取 类与元类
///+ 回来值有一下三种状况
///+  1. clas
///+  2. nil (该类有一个缺失的弱链接的父类)
///+  3. 其他 (这块空间预留给未来的类,例如前向引证)
Class readClass(Class cls, **bool** headerIsBundle, **bool** headerIsPreoptimized)
{
    ///+ 获取类名
  const char *mangledName = cls->nonlazyMangledName();
  if (missingWeakSuperclass(cls)) {
    ///+ 若该类没有父类(有或许是基类,也或许是该类在承继链上缺失父类)
    // No superclass (probably weak-linked).
    // Disavow any knowledge of this subclass.
    ///+ 增加到重映射类列表中
    addRemappedClass(cls, nil);
    cls->setSuperclass(nil);
    return nil;
  }
    Class replacing = nil;
  if (mangledName != nullptr) {
        ///+ 这部分看起来十分像类读取的过程,但在调试过程中发现并没有走到这里。所以疏忽
        ...
  }
    if (headerIsPreoptimized && !replacing) {
    // class list built in shared cache
        ///+ 类列表在同享缓存中构建
  } else {
    if (mangledName) { //some Swift generic classes can lazily generate their names
      ///+ 将 cls 增加到总表 gdb_objc_realized_classes 中
            ///+ 若存在类名抵触,则增加至 nomete_class_map
            ///+ <name: cls>
            ///+ 内部完结:`NXMapInsert(gdb_objc_realized_classes, name, cls);`
      addNamedClass(cls, mangledName, replacing);
    } else {
      ...
    }
    ///+ 将该类增加到在 runtime_init 就已开辟的表中
        ///+ 内部完结:objc::allocatedClasses.get().insert(cls)
        ///+ 内部完结还会调用(第二个参数默以为 true) addClassTableEntry(cls->ISA(), false)
    addClassTableEntry(cls);
  }
    // for future reference: shared cache never contains MH_BUNDLEs
  ///+ 假如这个 mach_header 是一个 bundle,给该类与元类打上符号
  ///+ 同享缓存绝不包括 MH_BUNDLE
  if (headerIsBundle) {
    const_cast<class_ro_t *>(cls->safe_ro())->flags |= RO_FROM_BUNDLE;
    const_cast<class_ro_t *>(cls->ISA()->safe_ro())->flags |= RO_FROM_BUNDLE;
  }
  return cls;
}

所以 addClass 首要就是讲 cls 增加到 runtime_init 中申请的表中。

那么,什么是 weak-linked class 呢?先看一下它是怎么判别的:

missingWeakSuperclass(Class cls)
{
  if (!cls->getSuperclass()) {
    // superclass nil. This is normal for root classes only.
        ///+ 没有父类,且不是根类
    return (!(cls->safe_ro()->flags & RO_ROOT));
  } else {
    // superclass not nil. Check if a higher superclass is missing.
        ///+ 父类存在,但在承继链更高的环节上没有父类,且不是根类
    Class supercls = remapClass(cls->getSuperclass());
    if (!supercls) return YES;
    if (supercls->isRealized()) return NO;
    return missingWeakSuperclass(supercls);
  }
}

很明显,在承继链上没有父类且不是根类的类则为 weak-linked class。我粘贴了一段概念,咱们能够去 原文 Frameworks and Weak Linking检查

One challenge faced by developers is that of taking advantage of new features introduced in new versions of OS X while still supporting older versions of the system. Normally, if an application uses a new feature in a framework, it is unable to run on earlier versions of the framework that do not support that feature. Such applications would either fail to launch or crash when an attempt to use the feature was made. Apple has solved this problem by adding support for weakly-linked symbols.

开发者在运用新版本系统中的新特性时仍需求棉铃兼容旧版本的挑战。通常状况下,app 在运用一个 framework 的新特性时,这是无法运转在不支撑这些特性的老版本中的。当履行到这些新特性代码时,app 或许溃散,或者是发动失利。Apple 经过增加支撑弱引证符号来解决这个问题。

When a symbol in a framework is defined as weakly linked, the symbol does not have to be present at runtime for a process to continue running. The static linker identifies a weakly linked symbol as such in any code module that references the symbol. The dynamic linker uses this same information at runtime to determine whether a process can continue running. If a weakly linked symbol is not present in the framework, the code module can continue to run as long as it does not reference the symbol. However, if the symbol is present, the code can use it normally. 当 framework 中的一个符号作为弱引证时,为了正常答应该符号在运转时不会表现出来。静态链接器在任何引证该符号的代码标识弱引证符号。动态链接器在运转时运用相同的信息来决定是否能够正常运转下去。假如弱引证符号不存在,只需代码中没有引证到该符号,程序能够正常运转下去。只需该符号存在,代码就能够正常运转。

If you are updating your own frameworks, you should consider making new symbols weakly linked. Doing so can make it easier for clients of your framework to support it. You should also make sure that your own code checks for the existence of weakly-linked symbols before using them.

开发者在更新自己的 framework 时,应该考虑创立新的弱引证符号。这样做让运用运用者跟简单运用。在运用之前应该检查代码中关于弱引证符号是否存在。

第四部分:重映射类
///+ 修正/重映射类
if (!noClassesRemapped()) {
    for (EACH_HEADER) {
        ///+ 从 “__objc_classrefs” 获取类偏重映射
        Class *classrefs = _getObjc2ClassRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
        // fixme why doesn't test future1 catch the absence of this?
        ///+ 从 “__objc_superrefs” 获取类偏重映射
        classrefs = _getObjc2SuperRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
    }
}

继续看

static void remapClassRef(Class *clsref)
{
  lockdebug::assert_locked(&runtimeLock);
  Class newcls = remapClass(*clsref);
    ///+ 修正/重映射这个类,与 sel 的修正相同
  if (*clsref != newcls) *clsref = newcls;
}
static Class remapClass(Class cls)
{
  lockdebug::assert_locked(&runtimeLock);
  if (!cls) return nil;
    ///+ 关于已辨认的前向引证,回来 <oldCls: newCls> 的表
    ///+ 关于已疏忽的弱链接的类,回来 nil
  auto *map = remappedClasses(NO);
  if (!map)
    return cls;
    ///+ 
  auto iterator = map->find(cls);
  if (iterator == map->end())
        ///+ 在 map 中未找到,直接回来 cls
    return cls;
    ///+ 若找到了,回来 <oldCls: newCls> 中的 newCls
    return std::get<1>(*iterator);
}
第五部分:修正 msg
for (EACH_HEADER) {
    ///+ 从 “__**objc_msgrefs” 区中获取 msg 引证**
    message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
    if (count == 0) continue;
    if (PrintVtables) {
        _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                     "call sites in %s", count, hi->fname());
    }
    for (i = 0; i < count; i++) {
        fixupMessageRef(refs+i);
    }
}

再看一下是怎么修正 msg 的:

static void
fixupMessageRef(message_ref_t *msg)
{
    ///+ 首先注册这些办法
  msg->sel = sel_registerName((const char *)msg->sel);  
  if (msg->imp == &objc_msgSend_fixup) {
    if (msg->sel == @selector(alloc)) {
      msg->imp = (IMP)&objc_alloc;
    } else if (msg->sel == @selector(allocWithZone:)) {
      msg->imp = (IMP)&objc_allocWithZone;
    } else if (msg->sel == @selector(retain)) {
      msg->imp = (IMP)&objc_retain;
    } else if (msg->sel == @selector(release)) {
      msg->imp = (IMP)&objc_release;
    } else if (msg->sel == @selector(autorelease)) {
      msg->imp = (IMP)&objc_autorelease;
    } else {
      msg->imp = &objc_msgSend_fixedup;
    }
  }
  else if (msg->imp == &objc_msgSendSuper2_fixup) {
    msg->imp = &objc_msgSendSuper2_fixedup;
  }
  else if (msg->imp == &objc_msgSend_stret_fixup) {
    msg->imp = &objc_msgSend_stret_fixedup;
  }
  else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
    msg->imp = &objc_msgSendSuper2_stret_fixedup;
  }
#if defined(__i386__) || defined(__x86_64__)
  else if (msg->imp == &objc_msgSend_fpret_fixup) {
    msg->imp = &objc_msgSend_fpret_fixedup;
  }
#endif
#if defined(__x86_64__)
  else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
    msg->imp = &objc_msgSend_fp2ret_fixedup;
  }
#endid
}

总结一下:

original imp sel repaired imp platform
objc_msgSend_fixup alloc objc_alloc All
allocWithZone: objc_allocWithZone: All
retain objc_retain All
release objc_release All
autorelease objc_autorelease All
otherwise objc_msgSend_fixedup All
objc_msgSendSuper2_fixup objc_msgSendSuper2_fixedup All
objc_msgSend_stret_fixup objc_msgSend_stret_fixedup All
objc_msgSendSuper2_stret_fixup objc_msgSendSuper2_stret_fixedup All
objc_msgSend_fpret_fixup objc_msgSend_fpret_fixedup i386, x86_64
objc_msgSend_fp2ret_fixedup objc_msgSend_fp2ret_fixedup x86_64
第六部分:读取 protocol
///+ 读取 protocol
for (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol;
    Class cls = (Class)&OBJC_CLASS_$_Protocol;
    ASSERT(cls);
    ///+ 初始化 protocol 哈希表(仅一次)
    NXMapTable *protocol_map = protocols();
    ///+ 该镜像是否被优化过协议
    bool isPreoptimized = hi->hasPreoptimizedProtocols();
    // Skip reading protocols if this is an image from the shared cache
    // and we support roots
    // Note, after launch we do need to walk the protocol as the protocol
    // in the shared cache is marked with isCanonical() and that may not
    // be true if some non-shared cache binary was chosen as the canonical
    // definition
    ///+ 假如当时镜像来此同享缓存而且咱们支撑 root,则越过协议读取流程
    ///+ Note:在发动之后,咱们需求遍历这些协议,由于同享缓存中的协议被 isCanonial() 符号,
    ///+ 但假如一些非同享缓存二进制作为标准界说是,这或许是不可信的。
    if (launchTime && isPreoptimized) {
        if (PrintProtocols) {
            _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                         hi->fname());
        }
        continue;
    }  
    ///+ 该镜像是否是一个 bundle
    bool isBundle = hi->isBundle();  
    ///+ 从 “__**objc_protolist” 区读取 protocol**
    protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    for (i = 0; i < count; i++) {
        ///+ 读取协议
        readProtocol(protolist[i], cls, protocol_map,
                     isPreoptimized, isBundle);
    }
}

很明显,要点在 readProtocol 中,来看一下:

static void
readProtocol(protocol_t *newproto, Class protocol_class,
      NXMapTable *protocol_map,
      bool headerIsPreoptimized, bool headerIsBundle)
{
  // This is not enough to make protocols in unloaded bundles safe,
  // but it does prevent crashes when looking up unrelated protocols.
  ///+ 这不能够保证在未加载的 bundle 中创立协议时的安全
  ///+ 但足以防止查找不相关的 protocol 时或许的溃散
  auto insertFn = headerIsBundle ? NXMapKeyCopyingInsert : NXMapInsert;  
  ///+ 经过协议名从哈希表中获取协议
  protocol_t *oldproto = (protocol_t *)getProtocol(newproto->mangledName);  
  ///+ 在哈希表中找到了协议
  if (oldproto) {
    if (oldproto != newproto) {
      // Some other definition already won.
      ///+ 别的的界说被承以为该 protocol
      // If we are a shared cache binary then we have a definition of this
      // protocol, but if another one was chosen then we need to clear our
      // isCanonical bit so that no-one trusts it.
      // Note, if getProtocol returned a shared cache protocol then the
      // canonical definition is already in the shared cache and we don't
      // need to do anything.
      ///+ 假如在同享缓存二进制文件中,存在这个协议的界说,但另一个当地的时分被挑选了
      ///+ 咱们需求整理 isCanonical,因而不能够信赖它
      ///+ Note:假如 getProtocol 回来了一个来自同享缓存中的协议,这意味着这个标准现已界说为同享缓存中
      ///+ Note:因而不需求做任何事情
      if (headerIsPreoptimized && !oldproto->isCanonical()) {
        // Note newproto is an entry in our __objc_protolist section which
        // for shared cache binaries points to the original protocol in
        // that binary, not the shared cache uniqued one.
        ///+ newproto 是 “__**objc_protolist” 区中的一个入口,**
        ///+ 关于同享缓存二进制文件来说,它指向该二进制文件中原始地址,而不是同享缓存中的仅有缓存
        auto cacheproto = (protocol_t *)
          getSharedCachePreoptimizedProtocol(newproto->mangledName);
        if (cacheproto && cacheproto->isCanonical())
          cacheproto->clearIsCanonical();
      }
    }
  }
  ///+ 协议表中没有找到协议 且 该镜像被预优化了
  else if (headerIsPreoptimized) {
    // Shared cache initialized the protocol object itself,
    // but in order to allow out-of-cache replacement we need
    // to add it to the protocol table now.  
    protocol_t *cacheproto = (protocol_t *)
      getPreoptimizedProtocol(newproto->mangledName);
    protocol_t *installedproto;
    ///+ 承认实在的 protocol
    if (cacheproto && cacheproto != newproto) {
      // Another definition in the shared cache wins (because
      // everything in the cache was fixed up to point to it).
      ///+ 承认在同享缓存中的另一个界说为该 protocol
      ///+ (由于缓存中的任何东西都现已被修正为指向其实在界说)
      installedproto = cacheproto;
    }
    else {
      // This definition wins.
      ///+ 承认当时这个界说为该 protocol
      installedproto = newproto;
    }  
    ASSERT(installedproto->getIsa() == protocol_class);
    ASSERT(installedproto->size >= sizeof(protocol_t));
    ///+ 已 <name: protocol> 的格局将该协议存至协议表中
    insertFn(protocol_map, installedproto->mangledName,
        installedproto);
  }
  ///+ 协议表中没有找到协议 且 该镜像未被预优化
  else {
    // New protocol from an un-preoptimized image. Fix it up in place.
    // fixme duplicate protocols from unloadable bundle
    ///+ 从未预优化的镜像中获取的协议。初始化后将其放入协议哈希表中
        ///+ (可是这里或许会 无法加载 bundle 存在重复协议的状况)
    newproto->initIsa(protocol_class); // fixme pinned
    insertFn(protocol_map, newproto->mangledName, newproto);
  }
}

总结下来,找到真正的 protocol 界说并增加到哈希表中。

第七部分:重映射协议
///+ 重映射协议
///+ 预优化镜像中的协议指向或许是正确的,但不承认
for (EACH_HEADER) {
    // At launch time, we know preoptimized image refs are pointing at the
    // shared cache definition of a protocol. We can skip the check on
    // launch, but have to visit @protocol refs for shared cache images
    // loaded later.
    ///+ 发动时,预优化镜像引证指向同享缓存中的协议界说,能够越过检查。
    ///+ 但在之后有必要访问同享缓存镜像中的协议引证
    if (launchTime && hi->isPreoptimized())
        continue;
    protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
    for (i = 0; i < count; i++) {
        ///+ 依照 {isCanonial() > 哈希表中的协议 > 预优化过的协议 > 匹配的 swift 协议} 优先级进行从头映射操作
        remapProtocolRef(&protolist[i]);
    }
}

除了在发动时越过预优化镜像中的引证之外,其他协议依照 {isCanonial() > 哈希表中的协议 > 预优化过的协议 > 匹配的 swift 协议} 优先级进行从头映射操作

第八部分:加载分类
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes. rdar://problem/53119145
///+ 加载分类
///+ 仅在初始分类附加操作完结时分履行。
///+ 关于发动时就出现的分类,加载操作将延迟到第一次 _dyld_objc_notify_register 中注册的 load_images 调用之后进行
if (didInitialAttachCategories) {
    for (EACH_HEADER) {
        load_categories_nolock(hi);
    }
}

很明显,要点在 load_categories_nolock,但仅在 load_images 调用之后履行,由于 didInitialAttachCategoriesload_iamges 中才会置为 true。

static void load_categories_nolock(header_info *hi) {
  ///+ 分类中是否包括类特点
  bool hasClassProperties = hi->info()->hasCategoryClassProperties();  
  size_t count;
  ///+ 界说一个函数指针
  auto processCatlist = [&](category_t * const *catlist) {
    for (unsigned i = 0; i < count; i++) {
      category_t *cat = catlist[i];
      ///+ 获取实在的类
      Class cls = remapClass(cat->cls);
      locstamped_category_t lc{cat, hi};  
      ///+ 类的方针类缺失,越过
      if (!cls) {
        continue;
      }  
      // Process this category.
      if (cls->isStubClass()) {
        // Stub classes are never realized. Stub classes
        // don't know their metaclass until they're
        // initialized, so we have to add categories with
        // class methods or properties to the stub itself.
        // methodizeClass() will find them and add them to
        // the metaclass as appropriate.
        ///+ stub 类永久不会被完结。
        ///+ stub 类知道被初始化才会知道其元类,因而将分类中的类办法与类特点增加到它们自身。
        ///+ methodizeClass() 将会寻觅这些类办法与类特点并增加到元类中。
        if (cat->instanceMethods ||
          cat->protocols ||
          cat->instanceProperties ||
          cat->classMethods ||
          cat->protocols ||
          (hasClassProperties && cat->_classProperties))
        {
          ///+ 将该协议增加到未解析的协议列表中
          ///+ 存储格局为 <cls: [(category, hi), ...]>
          objc::unattachedCategories.addForClass(lc, cls);
        }
      } else {
        // First, register the category with its target class.
        // Then, rebuild the class's method lists (etc) if
        // the class is realized.
        ///+ 首先,将分类注册到方针类
        ///+ 然后,假如类现已被完结了,从头构建类的办法列表
        ///+ 将协议中的实例办法,实例特点附加到类中
        if (cat->instanceMethods || cat->protocols
          || cat->instanceProperties)
        {
          if (cls->isRealized()) {
            ///+ 将分类附加到分类上
            attachCategories(cls, &lc, 1, ATTACH_EXISTING);
          } else {
            ///+ 将该协议增加到未解析的协议列表中
            ///+ 存储格局为 <cls: [(category, hi), ...]>
            objc::unattachedCategories.addForClass(lc, cls);
          }
        }  
        ///+ 将协议中的类办法,类特点附加到元类中
        if (cat->classMethods || cat->protocols
          || (hasClassProperties && cat->_classProperties))
        {
          if (cls->ISA()->isRealized()) {
            attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
          } else {
            ///+ 将该协议增加到未解析的协议列表中
            ///+ 存储格局为 <cls: [(category, hi), ...]>
            objc::unattachedCategories.addForClass(lc, cls->ISA());
          }
        }
      }
    }
  };  
  ///+ 调用上边界说的函数指针,增加该镜像中的分类
  processCatlist(hi->catlist(&count));
  processCatlist(hi->catlist2(&count));
}

总结起来:load_categories_nolock,遍历镜像中的分类列表:

  1. 逐一将分类中的类办法,类特点都增加到方针类的元类中
  2. 将实例办法,实例特点增加方针类中
  3. 假如方针类为 stub 类或还未完结,则先将该分类依照 <cls: [(category, hi), ...]> 的格局增加到未解析协议哈希表中。

然后看一下 attachCategories

static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags)
{
  /*
  * Only a few classes have more than 64 categories during launch.
  * This uses a little stack, and avoids malloc.
  *
  * Categories must be added in the proper order, which is back
  * to front. To 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. attachLists prepends the
  * lists, so the final result is in the expected order.
  */
  ///+ 在发动期间,只要很少部分类存在超过 64 个分类。运用较小的空间来防止空间浪费
  ///+
  ///+ 分类有必要依照其实际次序(在 compile list 中的次序)从后往前增加。
  constexpr uint32_t ATTACH_BUFSIZ = 64;
  ///+ 办法列表
  method_list_t  *mlists[ATTACH_BUFSIZ];
  ///+ 特点列表
  property_list_t *proplists[ATTACH_BUFSIZ];
  ///+ 协议列表
  protocol_list_t *protolists[ATTACH_BUFSIZ];
  uint32_t mcount = 0;
  uint32_t propcount = 0;
  uint32_t protocount = 0;
  bool fromBundle = NO;
  ///+ 是否增加到元类中
  bool isMeta = (flags & ATTACH_METACLASS);
  ///+ 获取类的额定存储空间
  auto rwe = cls->data()->extAllocIfNeeded();
  for (uint32_t i = 0; i < cats_count; i++) {
    auto& entry = cats_list[i];  
    ///+ 获取办法列表
    method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
    if (mlist) {
      if (mcount == ATTACH_BUFSIZ) { ///+ 当时缓存已达最大值,直接处理好已增加的办法
        ///+ 预备办法列表:将待处理列表中每个分类内部的办法依照地址排序
        prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
        ///+ 将协议中的办法增加至类中
        rwe->methods.attachLists(mlists, mcount);
        mcount = 0;
      }
     
      ///+ 未达到缓存最大值,先将分类中的办法逆序增加到待处理列表中
      mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
      fromBundle |= entry.hi->isBundle();
    }
    ///+ 获取特点列表
    property_list_t *proplist =
      entry.cat->propertiesForMeta(isMeta, entry.hi);
    if (proplist) {
      if (propcount == ATTACH_BUFSIZ) { ///+ 当时缓存已达最大值,直接将这些特点增加到类中
        ///+ 将协议中的特点增加类中
        rwe->properties.attachLists(proplists, propcount);
        propcount = 0;
      }
     
      ///+ 未达到缓存最大值,先将这个分类里的特点逆序增加到待处理列表中
      proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
    }
    ///+ 获取协议列表
    protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
    if (protolist) {
      if (protocount == ATTACH_BUFSIZ) { ///+ 当时缓存已达最大值,直接将这些协议增加到类中
        ///+ 将分类契合的协议增加至类中
        rwe->protocols.attachLists(protolists, protocount);
        protocount = 0;
      }
      ///+ 未达到缓存最大值,先将分类契合的协议增加逆序到待处理列表中
      protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
    }
  }
  ///+ 若存在未处理的分类办法,将他们增加到类中
  if (mcount > 0) {
    prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
             NO, fromBundle, __func__);
    rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
    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
        return !c->cache.isConstantOptimizedCache();
      });
    }
  }  
  ///+ 将这些特点增加到类中(逆序增加到待处理列表中的,所以前边部分为空)
  rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
 
  ///+ 将这些协议增加到类中(逆序增加到待处理列表中的,所以前边部分为空)
  rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}

能够看到,其实很简单,逐一解析分类,将办法、特点、协议均增加到待处理列表中,然后一致增加到类中。当待处理列表达到最大值时,优先将待处理列表中的办法、特点、协议都增加到类中。

明显,是经过 attachLists 将办法等增加到类中的:

void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;
    if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        uint32_t newCount = oldCount + addedCount;
        array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
        newArray->count = newCount;
        array()->count = newCount;  
        for (int i = oldCount - 1; i >= 0; i--)
            newArray->lists[i + addedCount] = array()->lists[i];
        for (unsigned i = 0; i < addedCount; i++)
            newArray->lists[i] = addedLists[i];
        free(array());
        setArray(newArray);
        validate();
    }
    else if (!list && addedCount == 1) {
        // 0 lists -> 1 list
        list = addedLists[0];
        validate();
    }
    else {
        // 1 list -> many lists
        Ptr<List> oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        for (unsigned i = 0; i < addedCount; i++)
            array()->lists[i] = addedLists[i];
        validate();
    }
}

其实很简单,尽管分为 0>1, 1>n, n>n 三种状况,但总是将需求增加的办法增加到数组的头部的。

因而,分类中的办法会掩盖类自身的同名办法,一起后加载分类会部分先加载分类的同名办法。

这是由于后增加的办法在数组前边,而办法调用时是早年往后找的。

那么假如咱们要运用本来的办法,咱们能够从办法后边往前查找。

比方这样

@implementation SomeObject
- (void)testMethod {
    NSLog(@"履行了原始办法");
}
@end
@implementation SomeObject (Cat)
- (void)testMethod {
    NSLog(@"履行了分类办法");
}
@end
...
unsigned int count;
Method *methods = class_copyMethodList(SomeObject.class, &count);
for (int i = count - 1; i >= 0; --i) {
    Method m = methods[i];
    if (method_getName(m) == @selector(testMethod)) {
        IMP imp = method_getImplementation(m);
        void (*func)(id) = (void *)imp;
        func(obj);
        break;
    }
}
...

阿里、字节:一套高效的iOS面试题(一 - runtime 结构模型 - 中)

第九部分:完结即时类(非延时类)

什么叫非延时类呢?有些类是有必要要在加载镜像时立马加载的,而有些类能够晚些时分再加载(真正用到的时分再加载)。

  • 完结了 +load 办法的类是为即时类
  • 未完结类则为延迟类
// Realize non-lazy classes (for +load methods and static instances)
///+ 完结即时类(非延迟类)
for (EACH_HEADER) {
    ///+ 获取即时类列表
    classref_t const *classlist = hi->nlclslist(&count);
    for (i = 0; i < count; i++) {
        ///+ 获取实在的类
        Class cls = remapClass(classlist[i]);
        if (!cls) continue;
        ///+ 将类与其元类增加到一切类的表,之所以会增加元类,由于第二个参数默以为true
        addClassTableEntry(cls);
        ///+ 完结非 swift 类
        realizeClassWithoutSwift(cls, nil);
    }
}
  1. 首先将类及其元类增加至一切类的哈希表中
  2. 然后完结该类

很明显,类的完结操作实在 realizeClassWithSwift 办法中:

///+ 履行类的初次初始化流程,包括开启 rw 空间,但不包括任何 swift 的初始化流程
///+ @return 回来该类的实在结构体
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
  lockdebug::assert_locked(&runtimeLock);
  class_rw_t *rw;
  Class supercls;
  Class metacls;
  if (!cls) return nil;
  ///+ 假如该类已被完结,验证这个类
  if (cls->isRealized()) {
    validateAlreadyRealizedClass(cls);
    return cls;
  }
  ASSERT(cls == remapClass(cls));
  ///+ 获取只读 ro
  auto ro = cls->safe_ro();
  ///+ 是否为元类
  auto isMeta = ro->flags & RO_META;
  if (ro->flags & RO_FUTURE) {
    // This was a future class. rw data is already allocated.
    ///+ future 类是一个指定类名的类,仅有 rw 空间与 RO_FUTURE 符号。已提前分配好空间,等候写入数据,适用于懒加载的类
    rw = cls->data();
    ro = cls->data()->ro();
    ASSERT(!isMeta);
    ///+ 修改其 flag。假如 falg 为后者,则修改为前者
    cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
  } else {
    // Normal class. Allocate writeable class data.
    ///+ 这是一个一般类,构建该类的结构体.
    rw = objc::zalloc<class_rw_t>();
    rw->set_ro(ro);
    rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
    cls->setData(rw);
  }
  ///+ 将 cla.cache 初始化为空
  cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
  ///+ 假如是元类。将 cls.cache 符号为 FAST_CACHE_META
#if FAST_CACHE_META
  if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
  // Choose an index for this class.
  // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
  ///+ 为该类挑选一个 index
  ///+ 假如没有 index 可用,符号该类及其一切子类需求原始 isa
  cls->chooseClassArrayIndex();
  // Realize superclass and metaclass, if they aren't already.
  // This needs to be done after RW_REALIZED is set above, for root classes.
  // This needs to be done after class index is chosen, for root metaclasses.
  // This assumes that none of those classes have Swift contents,
  //  or that Swift's initializers have already been called.
  //  fixme that assumption will be wrong if we add support
  //  for ObjC subclasses of Swift classes.
  ///+ 假如父类与元类还未完结,则完结他们。
  ///+ 关于根类来说,这需求在 RW_REALIZED 设置完结之后完结。
  ///+ 关于跟元类来说,这需求在类 index 设定之后完结。
  ///+ 这保证没有类包括 swift 内容,或者 swift 初始化流程早已完结。
  ///+ 完结其父类
  supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
  ///+ 完结其元类
  metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
  if (isMeta) {
    // Metaclasses do not need any features from non pointer ISA
    // This allows for a faspath for classes in objc_retain/objc_release.
    ///+ 假如是元类,设置该类需求原始 isa
    ///+ 元类不需求任何 non-pointer 的特性
    ///+ 这能够让这些类能够快速 retain 与 release
    cls->setInstancesRequireRawIsa();
  } else {
    // Disable non-pointer isa for some classes and/or platforms.
    // Set instancesRequireRawIsa.
    ///+ 某些平台上,一些特殊的类需求禁用 non-pointer
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    bool rawIsaIsInherited = false;
    static bool hackedDispatch = false;
    if (DisableNonpointerIsa) {
      // Non-pointer isa disabled by environment or app SDK version
      instancesRequireRawIsa = true;
    }
    else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
    {
      // hack for libdispatch et al - isa also acts as vtable pointer
      hackedDispatch = true;
      instancesRequireRawIsa = true;
    }
    else if (supercls && supercls->getSuperclass() &&
        supercls->instancesRequireRawIsa())
    {
      // This is also propagated by addSubclass()
      // but nonpointer isa setup needs it earlier.
      // Special case: instancesRequireRawIsa does not propagate
      // from root class to root metaclass
      instancesRequireRawIsa = true;
      rawIsaIsInherited = true;
    }
    ///+ 将该类与其一切子类都设置为禁用 non-pointer
    ///+ 在以上三种状况适用:
    ///+  1. non-pointer 被环境变量或 sdk 禁用
    ///+  2. libdispatch 被攻击了(此刻 isa 仍然可作为虚函数表指向)
    ///+  3. 该类的父类需求禁用 non-pointer
    if (instancesRequireRawIsa) {
      cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
    }
  }
// SUPPORT_NONPOINTER_ISA
#endif
  // Update superclass and metaclass in case of remapping
  ///+ 设置父类
  cls->setSuperclass(supercls);
  ///+ 将该类 isa 指向元类
  cls->initClassIsa(metacls);
  // Reconcile instance variable offsets / layout.
  // This may reallocate class_ro_t, updating our ro variable.
  ///+ 假如存在父类且不是元类,则使实例变量的偏移与内部布局标准化
  ///+ 这或许会从头分类 ro 的内存空间,并更新 ro 变量
  ///+  假如该类的 ro 的起始地址 【大于】 父类 ro 的起始地址,需求将该类的 ro 顺位移动
  if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);  
  // Set fastInstanceSize if it wasn't set already.
  ///+ 设置其 instanceSize
  cls->setInstanceSize(ro->instanceSize);  
  // Copy some flags from ro to rw
  ///+ 将 has_cxx+dtor 与 has_cxx_ctor 符号按需复制至 rw
  if (ro->flags & RO_HAS_CXX_STRUCTORS) {
    cls->setHasCxxDtor();
    if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
      cls->setHasCxxCtor();
    }
  }  
  // Propagate the associated objects forbidden flag from ro or from
  // the superclass.
  ///+ 从父类承继 【禁用相关目标】符号
  if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
    (supercls && supercls->forbidsAssociatedObjects()))
  {
    rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
  }
  // Connect this class to its superclass's subclass lists
  if (supercls) {
    ///+ 将该类增加至父类的 ext_rw 中的 firstSubclass,本来的 firstSubclass 移动至 nextSiblingClass
    ///+ 并从父类承继一些符号:
    ///+  FAST_CACHE_HAS_CXX_CTOR: 是否存在 C++ 结构办法
    ///+  FAST_CACHE_HAS_CXX_DTOR: 是否存在 C++ 析构办法
    ///+  FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否存在自界说的 dealloc 办法
    ///+  RW_NOPREOPT_CACHE: 是否答应 cache 预优化
    ///+  RW_NOPREOPT_SELSFAST_CACHE_REQUIRES_RAW_ISA: 是否禁用 non-pointer
    addSubclass(supercls, cls);
  } else {
    ///+ 根类:与一般类相似,但 firstSubclass 为大局变量
    addRootClass(cls);
  }
  // Attach categories
  ///+ 将类 ro 中的办法,特点,协议均增加至 rw.ext
  ///+ 从 objc::unattachedCategories 依据方针类查找分类并附加到类中
  methodizeClass(cls, previously);  
  return cls;
}
  1. 构建类的结构体
  2. 将类的 cache 初始化为 empty
  3. 假如是元类,打上 FAST_CACHE_META 符号
  4. 为该类挑选一个 index。若无可用 index,符号该类及其一切子类禁用 non-pointer
  5. 优先完结其父类与元类
  6. 依据需求或许会禁用 non-pointer
    • non-pointer 被环境变量或 sdk 禁用
    • libdispatch 等库被攻击了(此刻 isa 仍然可作为虚函数表指向)
    • 该类的父类需求禁用 non-pointer
  7. 设置其父类
  8. 将该类的 isa 指向其元类
  9. 依据父类的 instanceSize 按需移动成员变量的内存地址
  10. 设置其 instanceSize()
  11. 将 ro 中的 has_cxx+dtor 与 has_cxx_ctor 符号到 rw 中
  12. 从父类承继 【禁用相关目标】符号
  13. 将该类增加至其父类的子类列表(假如是根类,则增加到大局的根类列表中),并承继一些符号:
    • FAST_CACHE_HAS_CXX_CTOR: 是否存在 C++ 结构办法
    • FAST_CACHE_HAS_CXX_DTOR: 是否存在 C++ 析构办法
    • FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否存在自界说的 dealloc 办法
    • RW_NOPREOPT_CACHE: 是否答应 cache 预优化
    • RW_NOPREOPT_SELSFAST_CACHE_REQUIRES_RAW_ISA: 是否禁用 non-pointer
  14. 将 ro 中的办法,特点,协议均增加至 rw.ext,并从 objc::unattachedCategories 依据方针类查找分类并附加到类中

再看一眼 methodizeClass,从姓名看,将办法真正增加至类中:

static void methodizeClass(Class cls, Class previously)
{
  lockdebug::assert_locked(&runtimeLock);
  bool isMeta = cls->isMetaClass();
  auto rw = cls->data();
  auto ro = rw->ro();
  auto rwe = rw->ext();
  // Install methods and properties that the class implements itself.
  method_list_t *list = ro->baseMethods;
  if (list) {
    prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
    if (rwe) rwe->methods.attachLists(&list, 1);
  }  
  property_list_t *proplist = ro->baseProperties;
  if (rwe && proplist) {
    rwe->properties.attachLists(&proplist, 1);
  }
  protocol_list_t *protolist = ro->baseProtocols;
  if (rwe && protolist) {
    rwe->protocols.attachLists(&protolist, 1);
  }  
  // Root classes get bonus method implementations if they don't have
  // them already. These apply before category replacements.
  ///+ 根元类增加没什么用的 initilize 办法
  if (cls->isRootMetaclass()) {
    // root metaclass
    addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
  }  
  // Attach categories.
    ///+ 将协议附加到类中
  if (previously) {
    if (isMeta) {
      objc::unattachedCategories.attachToClass(cls, previously,
                          ATTACH_METACLASS);
    } else {
      // When a class relocates, categories with class methods
      // may be registered on the class itself rather than on
      // the metaclass. Tell attachToClass to look for those.
      objc::unattachedCategories.attachToClass(cls, previously,
                          ATTACH_CLASS_AND_METACLASS);
    }
  }
  objc::unattachedCategories.attachToClass(cls, cls,
                      isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}

这办法没什么好说的,将 ro 与协议中的办法,特点,协议都增加到 rw.ext 中。

第十部分:完结待处理列表
///+ 完结上边增加的稍后处理类列表中的一切类
if (resolvedFutureClasses) {
    for (i = 0; i < resolvedFutureClassCount; i++) {
        Class cls = resolvedFutureClasses[i];
        ///+ 完结该类
        realizeClassWithoutSwift(cls, nil);
        ///+ 符号该类及其一切子类均禁用 non-pointer
        cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
    }
    free(resolvedFutureClasses);
}

这个 resolvedFutureClasses 是在 第三部分:加载类 中。

load_image

load_image 是在镜像加载完结时回调的。首要用于调用一切类与分类的 +laod 办法。

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    ///+ 现已完结了 dyld 告诉注册,能够开端加载分类了
  if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
    didInitialAttachCategories = true;
        ///+ 加载分类
    loadAllCategories();
  }  
  // Return without taking locks if there are no +load methods here.
    ///+ 经过判别镜像内的 类数量与分类数量 决定
  if (!hasLoadMethods((const headerType *)mh)) return;  
  recursive_mutex_locker_t lock(loadMethodLock);
  // Discover load methods
  {
    mutex_locker_t lock2(runtimeLock);
        ///+ 预备一切类与分类的 +load 办法
    ///+ 将其以 {cls,+load} 的结构体参加到数组中
    prepare_load_methods((const headerType *)mh);
  }  
  // Call +load methods (without runtimeLock - re-entrant)
    ///+ 调用预备好的 load 办法
  call_load_methods();
}
prepare_load_methods 预备 load 办法
void prepare_load_methods(const headerType *mhdr)
{
  size_t count, i;
  lockdebug::assert_locked(&runtimeLock);
  ///+ 获取一切即时类(非延时类)
  classref_t const *classlist =
    _getObjc2NonlazyClassList(mhdr, &count);
  for (i = 0; i < count; i++) {
        ///+ 以【父类 > 子类】的次序将 调用 add_category_to_loadable_list 将结构体 {cls, +load} 参加到数组中
    schedule_class_load(remapClass(classlist[i]));
  }
  ///+ 获取一切即时分类(非延时分类)
  category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
  for (i = 0; i < count; i++) {
    category_t *cat = categorylist[i];
    Class cls = remapClass(cat->cls);
    ///+ 疏忽 weak-linked 类的分类
    if (!cls) continue; // category for ignored weak-linked class
    ///+ 完结该分类的方针类(假如还没完结)
    realizeClassWithoutSwift(cls, nil);
    ASSERT(cls->ISA()->isRealized());
    ///+ 将结构体 {cls, +load} 参加到数组中
    add_category_to_loadable_list(cat);
  }
}
///+ 保证 【父类 > 子类】的次序
///+ 将该类符号为 RW_LOADED (1<<23)
static void schedule_class_load(Class cls)
{
  if (!cls) return;
  ASSERT(cls->isRealized()); // _read_images should realize
  if (cls->data()->flags & RW_LOADED) return;
  // Ensure superclass-first ordering
  schedule_class_load(cls->getSuperclass());  
  add_class_to_loadable_list(cls);
    ///+ 将该类符号为 RW_LOADED (1<<23)
  cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
  IMP method;  
  lockdebug::assert_locked(&loadMethodLock);  
  ///+ 依据 name 匹配获取 load 办法
  method = cls->getLoadMethod();
  if (!method) return; // Don't bother if cls has no +load method
 
  ///+ 依据需求扩容,初始值为 16
  if (loadable_classes_used == loadable_classes_allocated) {
    loadable_classes_allocated = loadable_classes_allocated*2 + 16;
    loadable_classes = (struct loadable_class *)
      realloc(loadable_classes,
               loadable_classes_allocated *
               sizeof(struct loadable_class));
  }
 
  ///+ 将 {cls, +load} 放入数组末尾
  loadable_classes[loadable_classes_used].cls = cls;
  loadable_classes[loadable_classes_used].method = method;
  loadable_classes_used++;
}

将一切即时类(非即时类)和即时分类(非延时分类)的方针类 依照 【父类 > 子类】的次序把 {cls, +load} 增加到数组中,并将这些类符号为 RW_LOADED(1<< 23)

call_load_methods 履行 +load 办法
void call_load_methods(void)
{
  static bool loading = NO;
  bool more_categories;  
  lockdebug::assert_locked(&loadMethodLock);
  // Re-entrant calls do nothing; the outermost call will finish the job.
    ///+ 重复调不做任何事情;最外层调用将会完结操作
  if (loading) return;
  loading = YES;  
  ///+ 创立一个主动开释池
  void *pool = objc_autoreleasePoolPush();
  do {
    // 1. Repeatedly call class +loads until there aren't any more
    ///+ 1. 履行现已增加到 loadable_classes 中的一切 load 办法
    ///+  call_class_load 会将现有 loadable_classes 别离出来用于履行,
    ///+  新加的部分会在原有部分履行完结之后放入到新的 loadable_classes 中
    while (loadable_classes_used > 0) {
      call_class_loads();
    }
    // 2. Call category +loads ONCE
    ///+ 2. 履行现已增加到 loadable_categories 中一切的 load 办法
    ///+  call_category_loads 会将现有 loadable_categories 别离出来用于履行,
    ///+  内部会将新加的部分在原有部分履行完结之后会从头放入到新的 loadable_categories
    more_categories = call_category_loads();
    // 3. Run more +loads if there are classes OR more untried categories
    ///+ 3 履行新加的部分
  } while (loadable_classes_used > 0 || more_categories);
  ///+ 开释该主动开释池中的目标并开释该主动开释池
  objc_autoreleasePoolPop(pool);  
  loading = NO;
}
call_class_loads: 履行类的 load 办法
static void call_class_loads(void)
{
  int i;
 
  // Detach current loadable list.
  ///+ 将现有的列表别离出来
  struct loadable_class *classes = loadable_classes;
  int used = loadable_classes_used;
  loadable_classes = nil;
  loadable_classes_allocated = 0;
  loadable_classes_used = 0;
 
  // Call all +loads for the detached list.
  for (i = 0; i < used; i++) {
    Class cls = classes[i].cls;
    load_method_t load_method = (load_method_t)classes[i].method;
    if (!cls) continue;
        ///+ 履行 load 办法
    (*load_method)(cls, @selector(load));
  }
 
  // Destroy the detached list.
  ///+ 销毁别离出来的列表
  if (classes) free(classes);
}

将现有列表别离出来,履行后销毁。这不会影响履行期间新参加的部分

call_category_loads:履行分类的 load 办法
static bool call_category_loads(void)
{
  int i, shift;
  bool new_categories_added = NO;
 
  // Detach current loadable list.
  ///+ 将现有的别离出来
  struct loadable_category *cats = loadable_categories;
  int used = loadable_categories_used;
  int allocated = loadable_categories_allocated;
  loadable_categories = nil;
  loadable_categories_allocated = 0;
  loadable_categories_used = 0;
  // Call all +loads for the detached list.
  ///+ 履行现有的这部分分类的 load
  for (i = 0; i < used; i++) {
    Category cat = cats[i].cat;
    load_method_t load_method = (load_method_t)cats[i].method;
    Class cls;
    if (!cat) continue;  
    cls = _category_getClass(cat);
    ///+ 只要在类已注册的状况下才干履行
    ///+ 履行后将记载的 cat 置空
    if (cls && cls->isLoadable()) {
      (*load_method)(cls, @selector(load));
      cats[i].cat = nil;
    }
  }  
  // Compact detached list (order-preserving)
  ///+ 未履行 laod 办法的记载从头放入列表
  shift = 0;
  for (i = 0; i < used; i++) {
    if (cats[i].cat) {
      cats[i-shift] = cats[i];
    } else {
      shift++;
    }
  }
  used -= shift;
  // Copy any new +load candidates from the new list to the detached list.
  ///+ 履行期间新参加的记载追加到记载列表中
  new_categories_added = (loadable_categories_used > 0);
  for (i = 0; i < loadable_categories_used; i++) {
        ///+ 依据需求扩容
    if (used == allocated) {
      allocated = allocated*2 + 16;
      cats = (struct loadable_category *)
        realloc(cats, allocated *
                 sizeof(struct loadable_category));
    }
    cats[used++] = loadable_categories[i];
  }  
  // Destroy the new list.
  ///+ 先开释旧的列表
  if (loadable_categories) free(loadable_categories);  
  // Reattach the (now augmented) detached list.
  // But if there's nothing left to load, destroy the list.
  ///+ 假如存在未履行的记载,从头放入静态列表中
  ///+ 假如没有更多需求履行的记载,开释该列表
  if (used) {
    loadable_categories = cats;
    loadable_categories_used = used;
    loadable_categories_allocated = allocated;
  } else {
    if (cats) free(cats);
    loadable_categories = nil;
    loadable_categories_used = 0;
    loadable_categories_allocated = 0;
  } 
  ///+ 回来履行期间新参加到列表的数量
  return new_categories_added;
}
  • 将现有列表别离出来得到 cats
  • 履行列表内的 load 办法,因类未注册未履行的部分放到 cats 前边
  • 将履行期间新参加的记载追加到数组 cats 中
  • 将静态列表 loadable_categories 从头指向到 cats(期间会依据需求扩容),假如没有未履行的记载,则开释静态列表
  • 回来履行期间新参加列表的数目

unmap_image

unmap_image 是在 dyld 移除镜像时回调的。用于开释内存中的镜像,详细时刻是某个某个镜像不被任何进程依赖时调用。

void
unmap_image(const char *path __unused, const struct mach_header *mh)
{
  ///+ 一起锁住 loadMethodLock 与 runtimeLock
  recursive_mutex_locker_t lock(loadMethodLock);
  mutex_locker_t lock2(runtimeLock);
 
  unmap_image_nolock(mh);
}

能够看到,除了一起锁住 loadMethodLock 与 runtimeLock 之外,仅仅只要 unmap_image_load

void
unmap_image_nolock(const struct mach_header *mh)
{  
  header_info *hi;  
  // Find the runtime's header_info struct for the image
  ///+ 从当时镜像查找 header_info
  for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
    if (hi->mhdr() == (const headerType *)mh) {
      break;
    }
  }
    ///+ 若该镜像中不存在 header_info,也没啥好做的,直接 return
  if (!hi) return;
    ///+ 移除镜像内部的引证:
  _unload_image(hi);
  // Remove header_info from header list
    ///+ 从 header_info 链表中移除该镜像的 header_info
  removeHeader(hi);
    ///+ 开释该 header_info
  free(hi);
}

所以,要点在就在 _unload_image 中:

void _unload_image(header_info *hi)
{
  size_t count, i;
  lockdebug::assert_locked(&loadMethodLock);
  lockdebug::assert_locked(&runtimeLock);
  // Unload unattached categories and categories waiting for +load.
  ///+ 移除未附加的分类以及等候履行 +load 的分类
  // Ignore __objc_catlist2. We don't support unloading Swift
  // and we never will.
  ///+ 从 “__objc_catlist” 获取分类列表
  ///+ 疏忽归于 swfit 的 catlist2
  category_t * const *catlist = hi->catlist(&count);
  for (i = 0; i < count; i++) {
    category_t *cat = catlist[i];
    Class cls = remapClass(cat->cls);
        ///+ 疏忽弱链接类的分类
    if (!cls) continue; // category for ignored weak-linked class  
    // unattached list
    ///+ 从 unattachedCategories 列表中移除该分类
    objc::unattachedCategories.eraseCategoryForClass(cat, cls);  
    // +load queue
    ///+ 等候履行 +load 办法的分类列表中移除该分类
    remove_category_from_loadable_list(cat);
  }
  // Unload classes.
  ///+ 移除类
  // Gather classes from both __DATA,__objc_clslist
  // and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
  // only, and we need to unload that class if we unload an arclite image.
  ///+ 搜集一切类  
  objc::DenseSet<Class> classes{};
  classref_t const *classlist;  
  ///+ 从 “__**objc_classlist” 搜集一切类**
  classlist = _getObjc2ClassList(hi, &count);
  for (i = 0; i < count; i++) {
    Class cls = remapClass(classlist[i]);
    if (cls) classes.insert(cls);
  }  
  ///+ 从 “__**objc_nlclslist” 获取即时类(非延时类)**
  classlist = hi->nlclslist(&count);
  for (i = 0; i < count; i++) {
    Class cls = remapClass(classlist[i]);
    if (cls) classes.insert(cls);
  }  
  // First detach classes from each other. Then free each class.
  // This avoid bugs where this loop unloads a subclass before its superclass
  ///+ 首先将类互相别离,然后开释
  for (Class cls: classes) {
    ///+ 从 等候履行 load 办法的列表 loadable_classes 中将类移除
    remove_class_from_loadable_list(cls);
    ///+ 从还未附加分类表 unattachedCategories 中移除该类
    ///+ 将该类与其父类别离:从父类的子类列表中移除该类
    ///+ 将该类从类映射列表中移除
    ///+ 将该类从 allocatedClasses 中移除
   
    ///+ 针对元类做上述操作
    detach_class(cls->ISA(), YES);
    ///+ 针对类自身做上述操作
    detach_class(cls, NO);
  }
  for (Class cls: classes) {
    ///+ 开释元类
    free_class(cls->ISA());
    ///+ 开释类自身
    free_class(cls);
  }
}
  1. 移除分类
    • 未加载的分类
    • 等候履行 +load 办法的分类
  2. 移除类
    • 从等候履行 +load 办法的类列表中移除该类
    • 从还未附加分类分类表中移除该类
    • 与其父类别离:从父类的子类列表中移除该类(根类则从根类列表中移除)
    • 从类映射表中移除该类
    • 从 allocatedClasses 中移除该类
    • 开释该类

_objc_patch_root_of_class

给类打补丁,运用 replacementClass 替换 originalClass 中的一些信息,例如 isa, superclass:

void
_objc_patch_root_of_class(const struct mach_header *originalMH, void* originalClass,
             const struct mach_header *replacementMH, const void* replacementClass)
{
  mutex_locker_t lock(runtimeLock);
  return patch_root_of_class_nolock(originalMH, originalClass, replacementMH, replacementClass);
}
static void
patch_root_of_class_nolock(const struct mach_header *originalMH, void* originalClass,
         const struct mach_header *replacementMH, const void* replacementClass)
{
  Class originalCls = (Class)originalClass;
  Class replacementCls = (Class)replacementClass;
    ///+ 替换 isa(元类)
  originalCls->initIsa(replacementCls->getIsa());
    ///+ 替换 superclass
  originalCls->setSuperclass(replacementCls->getSuperclass());
    ///+ cache 初始化为空
  originalCls->cache.initializeToEmpty();  
  bool authRO = hasSignedClassROPointers((const headerType *)replacementMH);
  originalCls->bits.copyROFrom(replacementCls->bits, authRO);
}

能够发现,这部分尽管首要是类的加载流程,但也是 app 发动流程的一部分。到这里,其实 app 就现已预备好了,能够履行 main 函数了。更多能够检查 dyld 相关原理解析。