类的加载原理分析(上)

前提

在上个篇章我主要分析了应用程序的加载流程dyld(动态链接器)objc_init之间的响应关系,也引入到了我们熟悉的_dyld_objc_notify_register,这个正好我们在之前的篇章重识alloc流程有提到,下面dyld与objc_init是关系图

在这里插入图片描述
以下是_dyld_objc_notify_register的2个参数:
map_images:管理文件中和动态库中所有的符号,完成类class、方法selector、协议protocol、分类category的加载;也可以说是加载所有非懒加载类的数据

load_images:加载执行load方法

接下来我们就着重分析类的加载过程,在分析之前先简单分析一下objc_init中一些流程

一.objc_init分析

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();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    // map_images()
    // load_images()
#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

可以看到objc_init函数调用里面有多个函数的调用,看起来就是各种初始化,我们一一来简单分析其左右

1.environ_init()

作用:环境变量的初始化

代码如图,其实中间我注释的部分是我参考下面代码copy出来了的,为了方便验证是不是一些环境变量的信息

在这里插入图片描述
注入代码后,断点打印,可以看到很多环境变量的信息都出来了

在这里插入图片描述
可能大家对这边环境变量都很陌生,当然我也一样(😄),但是还是有那么几个有些眼熟的,比如
OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields(禁用非指针isa字段)

OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods(记录对class和category+load方法的调用)

这两个环境变量相信大家不陌生,下面一个我们正在了解,上面一个我们之前在类的分析相关篇章也介绍过ISA指针64位中末尾就是判断是否是NONPOINTERISA,我们可以设置一下,如图

在这里插入图片描述
环境变量的配置,在scheme->Arguments->Environment Variables中配置。OBJC_DISABLE_NONPOINTER_ISA设置为YES。然后大家在查看类的isa时,转换成64位,就可以发现尾数变为0,这里我就不给大家做示范了

2.tls_init()

作用:线程栈的创建初始化,线程的绑定

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}

_objc_pthread_destroyspecific

void _objc_pthread_destroyspecific(void *arg)
{
    _objc_pthread_data *data = (_objc_pthread_data *)arg;
    if (data != NULL) {
        _destroyInitializingClassList(data->initializingClasses);
        _destroySyncCache(data->syncCache);
        _destroyAltHandlerList(data->handlerList);
        for (int i = 0; i < (int)countof(data->printableNames); i++) {
            if (data->printableNames[i]) {
                free(data->printableNames[i]);  
            }
        }
        free(data->classNameLookups);

        // add further cleanup here...

        free(data);
    }
}

3.static_init()

作用:全局静态c++函数调用,只是调用自己的,为什么在这里调用,是为了在下面load_image之前也就是在dyld调⽤我们的静态构造函数之前,自己先调用

static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
    auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        UnsignedInitializer init(offsets[i]);
        init();
    }
}

4.runtime_init()

作用:初始化2张表,unattachedCategories.init分类表的初始化,allocatedClasses.init进行内存中类表的创建

void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}

5.runtime_init()

作用:初始化2张表,unattachedCategories.init分类表的初始化,allocatedClasses.init进行内存中类表的创建

void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}

6.exception_init()

作用:完成objc异常处理系统的初始化,进行回调函数的设置,实现异常捕获处理。

void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}

_objc_terminate

static void _objc_terminate(void)
{
    if (PrintExceptions) {
        _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
        // No current exception.
        (*old_terminate)();
    }
    else {
        // There is a current exception. Check if it's an objc exception.
        @try {
            __cxa_rethrow();
        } @catch (id e) {
            // It's an objc object. Call Foundation's handler, if any.
            (*uncaught_handler)((id)e);
            (*old_terminate)();
        } @catch (...) {
            // It's not an objc object. Continue to C++ terminate.
            (*old_terminate)();
        }
    }
}

当出现一个异常,会判断是否为objc异常,如果是objc异常会执行回调函数uncaught_handler。全局搜索uncaught_handler代码如下

objc_uncaught_exception_handler 
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
{
    objc_uncaught_exception_handler result = uncaught_handler;
    uncaught_handler = fn;
    return result;
}

在OC层代码,我们可以通过调用方法NSSetUncaughtExceptionHandler设置回调函数,回调函数会被赋值给uncaught_handler

7.cache_t::init()

作用:缓存条件环境的初始化。

void cache_t::init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
    mach_msg_type_number_t count = 0;
    kern_return_t kr;

    while (objc_restartableRanges[count].location) {
        count++;
    }

    kr = task_restartable_ranges_register(mach_task_self(),
                                          objc_restartableRanges, count);
    if (kr == KERN_SUCCESS) return;
    _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
                kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}

8._imp_implementationWithBlock_init()

作用:启动回调机制。通常这不会做什么,因为所有的初始化都是惰性的,但是在某些进程,我们会迫不及待地加载trampolines dylib。其实也是看下面注释大致翻译一下。。

void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
//在某些进程中急切地加载libobjc-trampolines.dylib。一些程序(最著名的是早期版本的嵌入铬)启用高度限制的沙盒配置文件阻止访问那个动态库。如果有什么事imp\u implementationWithBlock(正如AppKit已经开始做的那样),然后我们将我想把它装进去。在这里加载可以在沙箱之前设置它,配置文件已启用并阻止它。

    // Eagerly load libobjc-trampolines.dylib in certain processes. Some
    // programs (most notably QtWebEngineProcess used by older versions of
    // embedded Chromium) enable a highly restrictive sandbox profile which
    // blocks access to that dylib. If anything calls
    // imp_implementationWithBlock (as AppKit has started doing) then we'll
    // crash trying to load it. Loading it here sets it up before the sandbox
    // profile is enabled and blocks it.
    //
    // This fixes EA Origin (rdar://problem/50813789)
    // and Steam (rdar://problem/55286131)
    if (__progname &&
        (strcmp(__progname, "QtWebEngineProcess") == 0 ||
         strcmp(__progname, "Steam Helper") == 0)) {
        Trampolines.Initialize();
    }
#endif
}

至于_dyld_objc_notify_register我们在上一个篇章已经介绍过了,接下来的重点就是分析map_images也就是类的加载的核心部分

二._read_images引入分析

说明:不知道大家有没有发现_dyld_objc_notify_register的三个参数只有map_images带了取地址符号&,其实说白了就是指针传递 ,因为有其他地方可能也在调用时map_images发生变化,能够跟随变化,也就是地址拷贝,确保实时更新

1._read_images引入

map_images代码

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);
}

map_images_nolock代码如图

在这里插入图片描述
可以看到重点在于_read_images,看名字也知道是读取镜像文件,可不就是加载类吗😄,不过由于_read_images函数的代码内容太多了,很多地方我只能省略掉,只看一些关键的注释说明,大家可以自己查阅

2._read_images流程分析

// 重点
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;
    Class *resolvedFutureClasses = nil;
    size_t resolvedFutureClassCount = 0;
    static bool doneOnce;
    bool launchTime = NO;
    TimeLogger ts(PrintImageTimes);

    runtimeLock.assertLocked();

#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++
	 // 1.条件控制,进行一次的加载。将所有类放入一个表中
    if (!doneOnce) {
    	。。。。。。
        ts.log("IMAGE TIMES: first time tasks");
        。。。。。。
    }

    // Fix up @selector references
    // sel 名字 + 地址
    //2.修复sel。因为sel 在不同的image或者说不同文件 名字可能相同但地址不同。需要修复他实际对应的imp
    static size_t UnfixedSelectors;
    {
		。。。。。。。
    }

    ts.log("IMAGE TIMES: fix up selector references");

    // Discover classes. Fix up unresolved future classes. Mark bundle classes.
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
	// 3.初始化名称 - 错误混乱的类处理
    for (EACH_HEADER) {
	。。。。。。。
    }

    ts.log("IMAGE TIMES: discover classes");

    // Fix up remapped classes
    // Class list and nonlazy class list remain unremapped.
    // Class refs and super refs are remapped for message dispatching.
    //修复重新映射的类,类列表和非惰性类列表保持未映射状态,类引用和超级引用被重新映射以进行消息调度。
    if (!noClassesRemapped()) {
		。。。。。。
    }

    ts.log("IMAGE TIMES: remap classes");

#if SUPPORT_FIXUP
	// 5.修复旧的objc\u msgSend\u fixup调用站点 消息objc
    // Fix up old objc_msgSend_fixup call sites
    for (EACH_HEADER) {
		。。。。。。
    }

    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif

	// 6.发现协议。修复协议引用。
    // Discover protocols. Fix up protocol refs.
    for (EACH_HEADER) {
		。。。。。。
    }

    ts.log("IMAGE TIMES: discover protocols");

	// 7.修复没有被加载的协议
    // Fix up @protocol references
    // Preoptimized images may have the right 
    // answer already but we don't know for sure.
    for (EACH_HEADER) {
		。。。。。
    }

    ts.log("IMAGE TIMES: fix up @protocol references");

    // 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
    //发现类别。仅在初始类别之后执行此操作,附件已完成。对于启动时出现的类别,发现将延迟到之后的第一个load\u images调用,对动态对象通知寄存器的调用完成。
    // 8.分类处理
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }

    ts.log("IMAGE TIMES: discover categories");

    // Category discovery MUST BE Late to avoid potential races
    // when other threads call the new category code before
    // this thread finishes its fixups.

    // +load handled by prepare_load_methods()

    // Realize non-lazy classes (for +load methods and static instances)
    // 9.类的加载处理 类实现
    for (EACH_HEADER) {
   		 。。。。。。
            //实现类 重点
            realizeClassWithoutSwift(cls, nil);
		。。。。。
    }

    ts.log("IMAGE TIMES: realize non-lazy classes");
	// 10.没有被处理的类 优化那些被侵犯的类
    // Realize newly-resolved future classes, in case CF manipulates them
    if (resolvedFutureClasses) {
		。。。。。
    }

    ts.log("IMAGE TIMES: realize future classes");

    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }

    // Print preoptimization statistics
    if (PrintPreopt) {
		。。。。。。。
    }

#undef EACH_HEADER
}

相信大家都发现了,其实我把一些判断里面的代码不去看,其实每一个模块都有一个打印日志,以及响应的一些解释,没必要都去看,因为这样会迷失自己探究的初衷,我们需要的是关于类加载读取相关的函数调用,当然接下注释也可以大致判断_read_images到底做了那些事情:

  • 1: 条件控制进⾏⼀次的加载
  • 2: 修复预编译阶段的 sel 的混乱问题
  • 3: 错误混乱的类处理
  • 4:修复重映射⼀些没有被镜像⽂件加载进来的类
  • 5: 修复⼀些消息!
  • 6: 当我们类⾥⾯有协议的时候 :readProtocol
  • 7: 修复没有被加载的协议
  • 8: 分类处理
  • 9: 类的加载处理
  • 10 : 没有被处理的类 优化那些被侵犯的类

通过代码的一些注释大致总结出了上面的10个步骤,相信我们也能很清晰的发现,真正需要我们关心的是最后面3条,但是在看这些部分之前也发现了一个有用的函数就是readClass

3._read_images部分代码分析理解

第一部分:条件控制进⾏⼀次的加载

if (!doneOnce) {
        doneOnce = YES;
        launchTime = YES;

#if SUPPORT_NONPOINTER_ISA
        // Disable non-pointer isa under some conditions.

# if SUPPORT_INDEXED_ISA
        // Disable nonpointer isa if any image contains old Swift code
        for (EACH_HEADER) {
            if (hi->info()->containsSwift()  &&
                hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
            {
                DisableNonpointerIsa = true;
                if (PrintRawIsa) {
                    _objc_inform("RAW ISA: disabling non-pointer isa because "
                                 "the app or a framework contains Swift code "
                                 "older than Swift 3.0");
                }
                break;
            }
        }
# endif

# if TARGET_OS_OSX
        // Disable non-pointer isa if the app is too old
        // (linked before OS X 10.11)
//        if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_11)) {
//            DisableNonpointerIsa = true;
//            if (PrintRawIsa) {
//                _objc_inform("RAW ISA: disabling non-pointer isa because "
//                             "the app is too old.");
//            }
//        }

        // Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
        // New apps that load old extensions may need this.
        for (EACH_HEADER) {
            if (hi->mhdr()->filetype != MH_EXECUTE) continue;
            unsigned long size;
            if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
                DisableNonpointerIsa = true;
                if (PrintRawIsa) {
                    _objc_inform("RAW ISA: disabling non-pointer isa because "
                                 "the app has a __DATA,__objc_rawisa section");
                }
            }
            break;  // assume only one MH_EXECUTE image
        }
# endif

#endif

        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        //重点 小对象的处理。后续会讲解
        initializeTaggedPointerObfuscator();

        if (PrintConnecting) {
            _objc_inform("CLASS: found %d classes during launch", totalClasses);
        }

        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        // objc::unattachedCategories.init(32);
        // objc::allocatedClasses.init();
        //4/3 是负载因子。 要开辟的空间 乘以4/3。就相当于后续插入的只占用3/4 就刚好
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

        ts.log("IMAGE TIMES: first time tasks");
    }

大致理解:当doneOnce为NO时,即第一次进来时,会进入if判断里面,然后将doneOnce修改为YES,所以说这个判断只会进行一次,即第一次进来时。这里会创建一个哈希表gdb_objc_realized_classes,所有的类将放入这个表中,目的是方便快捷查找类。gdb_objc_realized_classes是命名类并且不在dyld共享缓存中,无论是否实现。

第二部分:修复预编译阶段的 sel 的混乱问题

    static size_t UnfixedSelectors;
    {
        mutex_locker_t lock(selLock);
        for (EACH_HEADER) {
            if (hi->hasPreoptimizedSelectors()) continue;

            bool isBundle = hi->isBundle();
            // 不同的镜像文件加载 一个是macho sel。
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
                const char *name = sel_cname(sels[i]);
                //一个是从dyld加载出来的sel  说白了更新macho的一些sel。源文件可能会改。就是方法内容会改
                //也就是重定向
                SEL sel = sel_registerNameNoLock(name, isBundle);
                if (sels[i] != sel) {
                    sels[i] = sel;
                }
            }
        }
    }

大致理解:修复sel。因为sel 在不同的image或者说不同框架如Foundation、CoreFoundation方法名字可能相同但地址不同。需要修复他实际对应的imp

第三部分:错误混乱的类处理

    // Discover classes. Fix up unresolved future classes. Mark bundle classes.
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

    for (EACH_HEADER) {
        if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }

        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];
            //给cls 进行绑定
            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;
            }
        }
    }

4.由readClass获取探索思路

其实通过上面_read_Image的代码中可以发现,多出由调用readClass,而通过名字也可以判断,有点像是读取类的信息,那么我们就进去看看

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
//翻译
* * readClass
* 读取由编译器编写的类和元类。
* 返回新的类指针。 这可能是:
* - cls
* - nil(cls 缺少弱链接超类)
* - 别的东西(这个class的空间被未来的class保留了)
*
* 请注意,此功能执行的所有工作均由
* mustReadClasses()。 不要在不更新该功能的情况下更改该功能。
*
* Locking:由map_images或objc_readClassPair获取的runtimeLock
**********************************************************************/

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.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->setSuperclass(nil);
        return nil;
    }
    
    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    if (mangledName != nullptr) {
        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));

            // Manually set address-discriminated ptrauthed fields
            // so that newCls gets the correct signatures.
            newCls->setSuperclass(cls->getSuperclass());
            newCls->initIsa(cls->getIsa());

            rw->set_ro((class_ro_t *)newCls->data());
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->getName());
            free((void *)old_ro);

            addRemappedClass(cls, newCls);

            replacing = cls;
            cls = newCls;
        }
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
    } else {//一些 Swift 泛型类可以懒惰地生成它们的名称
        if (mangledName) { //some Swift generic classes can lazily generate their names
            addNamedClass(cls, mangledName, replacing);
        } else {
            Class meta = cls->ISA();
            const class_ro_t *metaRO = meta->bits.safe_ro();
            ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
            ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
        }
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}

addNamedClass代码

/***********************************************************************
* 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
* 翻译
* 将 name => cls 添加到命名的非元类映射。
* 警告重复的类名并保留旧的映射。
* 锁定:runtimeLock 必须由调用者持有。
**********************************************************************/
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());
}

addClassTableEntry代码

/***********************************************************************
* 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.
* 翻译
* 将一个类添加到所有类的表中。 如果 addMeta 为真,
* 也会自动添加类的元类。
* 锁定:runtimeLock 必须由调用者持有。
**********************************************************************/
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.
    auto &set = objc::allocatedClasses.get();

    ASSERT(set.find(cls) == set.end());

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        addClassTableEntry(cls->ISA(), false);
}

其实以上这些对于目前我们来说并不重要,我也只是参照代码的注释进行了翻译,大致知道是干嘛的,但是我们关心的是什么?说白了还是我们自定义的类的加载过程,那么通过在readClass中添加一些判断就可以用来筛选自己的类,怎么去判断呢?首先就是打印mangledName,通过点进去查看大致可以判断这是返回的一个类名,当然大家也可以去打印验证,这里我就不展示了,直接得出判断方式如下

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->nonlazyMangledName();
    const char *LGPersonName = "LGPerson";

    if (strcmp(mangledName, LGPersonName) == 0) {
        // 普通写得类 他是如何
        printf("%s -YCX: 要研究的: - %s\n",__func__,mangledName);
    }
    。。。。。。。。。省略

断点打印结果如下

在这里插入图片描述
我们熟悉的LGperson它出来啦!!!而这就是我们接下来探索的思路所在,通过这种方式在多处,添加代码打印调试判断,就可以摸清楚整个的执行流程了

那么今天的探索就到次为止了,预知后续详情,请听下回分解😄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值