上篇文章,咱们学习了app加载时dyld的过程。dyld从start开始,递归初始化dyld_system
、dyld_dispatch
、dyld_obj
完成动态库的连接;最后进入obj_init
。c++
可是镜像文件在dyld中,objc_init在objc库里面,dyld的镜像文件如何读取出来映射到内存中,并以表的形式存储起来呢?swift
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
// 读取影响运行的环境变量,若是须要,还能够打印环境变量帮助
environ_init();
// 关于线程key的绑定--好比每线程数据的析构函数
tls_init();
// 运行系统的C++静态构造函数,在dyld调用咱们的静态构造函数以前,libc会调用_objc_init(),因此咱们必须本身作
static_init();
// 无源码,就是说objc的异常彻底才有c++那一套
lock_init();
// 初始化异常处理系统,好比注册异常的回调函数,来监控异常
exception_init();
// 仅供objc运行时使用,注册处理程序,以便在映射、取消映射和初始化objc镜像文件时调用
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
复制代码
读取影响运行的环境变量。若是须要,还能够打印环境变量帮助。数组
void environ_init(void)
{
... 代码省略
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}
复制代码
for循环代码是在知足必定条件下,打印环境变量内容、环境变量的注释、环境变量是否已设置等信息。缓存
OBJC_PRINT_LOAD_METHODS
能够监控全部的+load方法,从而处理启动优化OBJC_DISABLE_NONPOINTER_ISA
能够控制isa优化开关,从而优化整个内存结构export OBJC_HELP=1
查看Edit scheme -> Run -> Arguments
中设置环境变量。tls_init()
是关于线程key的绑定,好比线程数据的析构函数,安全
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
复制代码
static_init()
方法会运行C++静态构造函数(只会运行系统级别的构造函数)。在dyld调用咱们的静态构造函数以前,libc
会调用_objc_init()
,所以咱们必须本身作。bash
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors,
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
复制代码
lock_init()
方法是个空实现,有多是工厂重写/接口/预留/未开源,就是说objc的异常是彻底采用C++那一套app
void lock_init(void)
{
}
复制代码
exception_init()
初始化libobjc的异常回调系统,好比咱们后面会讲的能够注册异常的毁掉函数,从而监控异常的处理。ide
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
复制代码
调用只声明不实现不做任何处理的方法,就会报错,来到_objc_terminate。函数
加载全部类的信息时候咱们就要依赖这个注册函数的回调通知告诉dyld作了哪些事情,以及须要哪些环境,以及objc和dyld之间的通信,还就是当调用函数时候,系统执行的操做,以及当没有映射到的时候,系统应该如何操做。oop
//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
复制代码
objc-image-info
的镜像文件的数组,回调mapped函数;map_images
:dyld将image加载进内存时,会触发该函数。load_image
:dyld初始化image时,会触发该函数。unmap_image
:dyld将image移除时,会触发该函数。dyld将image加载进内存时,会触发该函数就,触发_dyld_objc_notify_register回掉。
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
复制代码
· hCount:镜像文件的个数
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
}
if (PrintImages) {
_objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}
// Find all images with Objective-C metadata.
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
// Size some data structures based on main executable`s size
#if __OBJC2__
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
#if SUPPORT_GC_COMPAT
// Halt if this is a GC app.
if (shouldRejectGCApp(hi)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"Objective-C garbage collection "
"is no longer supported.");
}
#endif
}
hList[hCount++] = hi;
if (PrintImages) {
_objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
hi->fname(),
mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
hi->info()->isReplacement() ? " (replacement)" : "",
hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
hi->info()->optimizedByDyld()?" (preoptimized)":"");
}
}
}
// Perform one-time runtime initialization that must be deferred until
// the executable itself is found. This needs to be done before
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later.
if (firstTime) {
sel_init(selrefCount);
arr_init();
#if SUPPORT_GC_COMPAT
// Reject any GC images linked to the main executable.
// We already rejected the app itself above.
// Images loaded after launch will be rejected by dyld.
for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[i];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"%s requires Objective-C garbage collection "
"which is no longer supported.", hi->fname());
}
}
#endif
#if TARGET_OS_OSX
// Disable +initialize fork safety if the app is too old (< 10.13).
// Disable +initialize fork safety if the app has a
// __DATA,__objc_fork_ok section.
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_13) {
DisableInitializeForkSafety = true;
if (PrintInitializing) {
_objc_inform("INITIALIZE: disabling +initialize fork "
"safety enforcement because the app is "
"too old (SDK version " SDK_FORMAT ")",
FORMAT_SDK(dyld_get_program_sdk_version()));
}
}
for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[i];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {
DisableInitializeForkSafety = true;
if (PrintInitializing) {
_objc_inform("INITIALIZE: disabling +initialize fork "
"safety enforcement because the app has "
"a __DATA,__objc_fork_ok section");
}
}
break; // assume only one MH_EXECUTE image
}
#endif
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
复制代码
又是很是长的一段源码,我的阅读源码的时候有几个小tips:
if-else
或者 while循环
中上段代码能够缩减为:while循环内都在操做hCount,_read_images()是核心代码。
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
复制代码
源码有400行,用上面的小tips分析缩减代码:
void _read_images {
// 1. 第一次进来 - 开始建立表
// gdb_objc_realized_classes : 全部类的表 - 包括实现的和没有实现的
// allocatedClasses: 包含用objc_allocateClassPair分配的全部类(和元类)的表。(已分配)
if (!doneOnce) {
doneOnce = YES;
// namedClasses
// Preoptimized classes don`t go in this table.
// 4/3 is NXMapTable`s load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
// 2. 读取全部类的列表
for (EACH_HEADER) {
classref_t *classlist = _getObjc2ClassList(hi, &count);
}
// 3. 获取全部的类引用
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
}
// 4. sel - 方法编号
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
}
// 5. 修复旧的objc_msgSend_fixup调用致使一些消息没有处理
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
}
// 6. 协议
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);
}
}
// 7. 修复协议重映射
// 获取全部的协议引用
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
// 8. 实现非惰性类(用于+ load方法和静态实例)
for (EACH_HEADER) {
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
}
// 9. 在CF基础上,实现将来类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 10. 分类
for (EACH_HEADER) {
category_t **catlist = _getObjc2CategoryList(hi, &count);
}
}
复制代码
实例化存储类的哈希表,而且根据当前类数量作动态扩容
第一次进来,建立两张表:gdb_objc_realized_classes
、allocatedClasses
// This is a misnomer: gdb_objc_realized_classes is actually a list of
// named classes not in the dyld shared cache, whether realized or not.
NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
复制代码
/
* allocatedClasses
* A table of all classes (and metaclasses) which have been allocated
* with objc_allocateClassPair.
/
static NXHashTable *allocatedClasses = nil;
复制代码
gdb_objc_realized_classes
:allocatedClasses
:objc_allocateClassPair
分配的全部类(和元类)的表(已分配)。从编译后的类列表中取出全部类,遍历进行处理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
for (EACH_HEADER) {
// 从编译后的类列表中取出全部类,获取到的是一个classref_t类型的指针
classref_t *classlist = _getObjc2ClassList(hi, &count);
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
for (i = 0; i < count; i++) {
// 数组中会取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系统类,例如CF、Fundation、libdispatch中的类。以及本身建立的类。
Class cls = (Class)classlist[i];
// 经过readClass函数获取处理后的新类,
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.
// 将懒加载的类添加到数组中
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
复制代码
来到本文的第一个核心内容:readClass
-- 获取处理后的新类
/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* Returns the new class pointer. This could be:
* - cls
* - nil (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
* Note that all work performed by this function is preflighted by
* mustReadClasses(). Do not change this function without updating that one.
*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
... 代码省略
return cls;
}
复制代码
根据小tips中的经过函数的返回值,反向寻找核心代码
,readClass的做用就是返回一个cls
根据英文注释
这个Class多是有三种类型:nil
/ popFutureNamedClass
/ cls
nil
:当前类的父类中有类是weak-linked的,而且已经missing的,则 cls 的全部信息也是不可信的, 因此将其添加到重映射表里,映射为nil。
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
复制代码
popFutureNamedClass
:正经常状况下不会走进popFutureNamedClass
判断,这是专门针对将来的待处理的类
的特殊操做,所以read_image不会对ro、rw进行操做(可打断点调试,建立类和系统类都不会进入)
// 只有在将来要处理的类才处理,测试方法是在里面打个断点,看看能不能进入到里面去,最终测试是没进去
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class`s struct.
// Preserve future`s rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can`t complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
复制代码
cls
调用add
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn`t work because of duplicates
// assert(cls == getClass(name));
assert(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
复制代码
gdb_objc_realized_classes
哈希表(存放全部类)/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
assert(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// assert(!cls->isRealized());
}
复制代码
allocatedClasses
哈希表(已初始化)/***********************************************************************
* addClassTableEntry
* Add a class to the table of all classes. If addMeta is true,
* automatically adds the metaclass of the class as well.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
static void addClassTableEntry(Class cls, bool addMeta = true) {
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
assert(!NXHashMember(allocatedClasses, cls));
if (!isKnownClass(cls))
NXHashInsert(allocatedClasses, cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
复制代码
readClass
主要做用:将当前类加入已经建立好的到gdb_objc_realized_classes
(存放全部类) 和allocatedClasses
(已初始化)两张表中
将未映射Class和Super Class重映射,被remap的类都是非懒加载的类
// 主要是修复重映射 - 通常走不进来
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 重映射Class,注意是从_getObjc2ClassRefs函数中取出类的引用
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?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
复制代码
将全部SEL都注册到namedSelectors
哈希表中,是另一张哈希表
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 注册SEL的操做
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
复制代码
for (EACH_HEADER) {
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++) {
// 内部将经常使用的alloc、objc_msgSend等函数指针进行注册,并fix为新的函数指针
fixupMessageRef(refs+i);
}
}
复制代码
遍历全部协议列表,而且将协议列表加载到Protocol的哈希表中
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Protocol类,全部协议和对象的结构体都相似,isa都对应Protocol类
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
// 获取protocol哈希表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 从编译器中读取并初始化Protocol
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
复制代码
修复协议列表引用,优化后的images多是正确的,可是并不肯定
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don`t know for sure.
for (EACH_HEADER) {
// 须要注意到是,下面的函数是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不同
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
复制代码
NonlazyClass is all about a class implementing or not a +load method.
根据苹果文档,实现了+ load方法
的类是非懒加载类,不然就是懒加载类。
+ load方法
是在main函数以前被调用的。这个时候为了能后保证+ load方法
能被调用,就必须提早把这个类加载好。全部非懒加载类如何初始化,进行rw、ro等操做的?
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
// 1. 取出非懒加载类
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// hack for class __ARCLite__, which didn`t get this above
#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
cls->cache._mask = 0;
cls->cache._occupied = 0;
}
if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache &&
(cls->ISA()->cache._mask || cls->ISA()->cache._occupied))
{
cls->ISA()->cache._mask = 0;
cls->ISA()->cache._occupied = 0;
}
#endif
// 2. 将类加入到表里,若是此类已经添加过,则再也不添加
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can`t disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 3. 实现全部非懒加载的类(实例化类对象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
复制代码
_getObjc2NonlazyClassList
获取到__objc_nlclslist,取出非懒加载类addClassTableEntry
再加载一遍——若是已添加就不会添加进去,确保整个结构都被添加realizeClassWithoutSwift
来到本文的第二个核心内容实现全部非懒加载
的类(实例化类对象的一些信息,例如rw)
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls)
{
if (!cls) return nil;
if (cls->isRealized()) return cls;
...代码省略
// Attach categories
methodizeClass(cls);
return cls;
}
复制代码
首先确认一点,咱们是从for循环里进入的realizeClassWithoutSwift
,当前循环结束的条件是 !cls
和 cls -> isRealized()
。
rw
表示readWrite
,因为动态性,可能会往类中添加属性、方法、添加协议ro
表示readOnly
,在编译时已经肯定了内存ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
复制代码
此时只是建立rw,并将ro赋值给rw->ro。rw里的methods
、 properties
、 protocols
仍然为空。
当前类
的父类
、元类
父类
,就经过addSubclass
把当前类
连接到超类
的子类列表中去不断的递归操做,完善类的继承链,递归结束的条件就是上文提到的if (!cls) return nil;
(根元类 -> NSObject -> nil)
supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); // 递归获取父类
metacls = realizeClassWithoutSwift(remapClass(cls->ISA())); // 递归获取元类
...
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls); // 父类与元类的归属关系
...
// Connect this class to its superclass`s subclass lists
if (supercls) {
addSubclass(supercls, cls); // 将此类连接到其超类的子类链表
} else {
addRootClass(cls);
}
复制代码
methodizeClass
会从ro中读取methods
、 properties
、 protocols
赋值给rw/***********************************************************************
* methodizeClass
* Fixes up cls`s method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data(); // 此时的rw中method list, protocol list, and property list仍然为空
auto ro = rw->ro;
···
// Install methods and properties that the class implements itself.
// 将rw->ro中的 method list, protocol list, and property list 赋值给 rw
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
···
}
复制代码
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
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;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
复制代码
多对多
:若是当前调用attachLists
的list_array_tt
二维数组中有多个一维数组realloc
容器扩容:旧的大小 + 新增的大小
旧的数据memmove
移动到容器的末尾
新的数据memcpy
拷贝到容器的起始位置
0对一
:若是调用attachLists的list_array_tt二维数组为空且新增大小数目为 1直接赋值addedList的第一个list
一对多
:若是当前调用attachLists的list_array_tt二维数组只有一个一维数组realloc
容器扩容:旧的大小 + 新增的大小
因为只有一个一维数组,旧数据直接赋值到新容器的末尾
新的数据memcpy
拷贝到容器的起始位置
memmove
和memcpy
有什么区别,为何要先memmove
后memcpy
:memmove
进行内存平移,保证内存结构安全memcpy
从原内存地址的起始位置开始拷贝若干个字节到目标内存地址中,速度快。ro
表示readOnly
,属于类的自己数据,最基础的数据。在编译时已经肯定了内容,不会被改变rw
表示readWrite
,因为OC的动态性,可能会往类中添加属性、方法、添加协议。attachLists
,还有哪些状况methodizeClass
:类的加载 - 处理方法/属性/协议addMethods
:添加方法_class_addProperty
:添加属性_class_addProtocol
:添加协议attachCategories
:添加分类咱们下篇文章会学习懒加载类
和分类`的加载。
本文主要讲了dyld的镜像文件经过_dyld_objc_notify_register(&map_images, load_images, unmap_image);
读取出来映射到内存中,并以表的形式存储起来的过程。
map_images
:dyld将image加载进内存时,会触发该函数。load_image
:dyld初始化image时,会触发该函数。unmap_image
:dyld将image移除时,会触发该函数。非懒加载类加载流程:
map_image
-> read_images
- realizeClassWithoutSwift
- methodlizeClass
-> attachLists
对rw赋值。