美文网首页
12、dyld与objc的关联

12、dyld与objc的关联

作者: 白马啸红中 | 来源:发表于2021-02-20 15:33 被阅读0次

环境说明objc-818.2源码MacOS 11.2.1系统,Xcode 12.4,调试可以参考objc-runtime源码编译,一步步来即可,感觉区别于objc-787.1的部分代码就是注销了的-。-。


上一篇文章APP的加载与dyld主要是研究app启动前到main方法调用都做了写什么,其中设计到跨库的调用了libobjc库中的runtime-new.mm文件中的_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();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

_objc_init方法中,在调用注册notify通知前,做了很多出事话的操作,对比上一章所叙述,可以知道这些初始化也是在加载app时就开始做了,那么到底出初始了哪些东西,就在这一章研究下。

1、_objc_init函数分析

一、environ_init()
/***********************************************************************
* environ_init
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
**********************************************************************/
void environ_init(void) 
{
    if (issetugid()) {
        // All environment variables are silently ignored when setuid or setgid
        // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
        return;
    } 

    bool PrintHelp = false;
    bool PrintOptions = false;
    bool maybeMallocDebugging = false;

    // Scan environ[] directly instead of calling getenv() a lot.
    // This optimizes the case where none are set.
    for (char **p = *_NSGetEnviron(); *p != nil; p++) {
        if (0 == strncmp(*p, "Malloc", 6)  ||  0 == strncmp(*p, "DYLD", 4)  ||  
            0 == strncmp(*p, "NSZombiesEnabled", 16))
        {
            maybeMallocDebugging = true;
        }

        if (0 != strncmp(*p, "OBJC_", 5)) continue;
        
        if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
            PrintHelp = true;
            continue;
        }
        if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
            PrintOptions = true;
            continue;
        }
        
        const char *value = strchr(*p, '=');
        if (!*value) continue;
        value++;
        
        for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
            const option_t *opt = &Settings[i];
            if ((size_t)(value - *p) == 1+opt->envlen  &&  
                0 == strncmp(*p, opt->env, opt->envlen))
            {
                *opt->var = (0 == strcmp(value, "YES"));
                break;
            }
        }            
    }

    // Special case: enable some autorelease pool debugging 
    // when some malloc debugging is enabled 
    // and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
    if (maybeMallocDebugging) {
        const char *insert = getenv("DYLD_INSERT_LIBRARIES");
        const char *zombie = getenv("NSZombiesEnabled");
        const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
        if ((getenv("MallocStackLogging")
             || getenv("MallocStackLoggingNoCompact")
             || (zombie && (*zombie == 'Y' || *zombie == 'y'))
             || (insert && strstr(insert, "libgmalloc")))
            &&
            (!pooldebug || 0 == strcmp(pooldebug, "YES")))
        {
            DebugPoolAllocation = true;
        }
    }

    PrintHelp = true;
    PrintOptions = true;
    // 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);
        }
    }
}

通过注释和方法名就大致可以猜出是初始化了环境和读取影响runtime的环境参数的,并且还可以打印一些环境变量。真正打印的部分在最后if判断中的for循环代码中,但是由于条件限制并不会打印出来,所以这里调试一下,将PrintHelpPrintOptions直接设置为true,并运行,直接打印:

环境变量打印 在这个之前,可以找到两个熟悉的环境参数,是在GDB时期就存在的,那就是MallocNSZombiesEnabled用来分析崩溃EXC_BAD_ACCESS时用到的。那怎么设置这边参数就知道了,在Product——>Scheme——>Edit Scheme...——>Run(Debug)——>Arguments——>Environment Variables项下添加参数:
添加环境变量 每个参数代表不同意义,Malloc将会记录申请过的内存临时变量不销毁,NSZombieEnable将允许空指针变量不被销毁,所以这两个变量不能长时间打开,内存扛不住。还有许多其他的环境参数如:
1、DYLD_PRINT_STATISTICS
设置 DYLD_PRINT_STATISTICSYES,控制台就会打印 app的加载时长,包括整体加载时长和动态库加载时长,即main函数之前的启动时间(查看pre-main耗时),可以通过设置了解其耗时部分,并对其进行启动优化。
2、OBJC_DISABLE_NONPOINTER_ISA
杜绝生成相应的nonpointer isa具体参考isa与对象、类的关系,生成的都是普通的isa
3、NSDoubleLocalizedStrings
项目做国际化本地化(Localized)的时候是一个挺耗时的工作,想要检测国际化翻译好的语言文字UI会变成什么样子,可以指定这个启动项。可以设置 NSDoubleLocalizedStrings为YES。
4、NSShowNonLocalizedStrings
在完成国际化的时候,偶尔会有一些字符串没有做本地化,这时就可以设置NSShowNonLocalizedStringsYES,所有没有被本地化的字符串全都会变成大写。
5、OBJC_PRINT_LOAD_METHODS
打印ClassCategory+ (void)load方法的调用信息。
二、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
}

TLSThread Local Storage,是一种通过key-value形式将线程存储起来的,在iOS中每个线程都拥有自己的TLS,负责保存本线程的一些变量, 且TLS无需锁保护。
这里初始化的就是当前执行_objc_init的线程TLS,也就是将执行_objc_init线程与他的存储地址相关联。

三、static_init()
/***********************************************************************
* 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]();
    }
    auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        UnsignedInitializer init(offsets[i]);
        init();
    }
}

根据注释描述可以知道,这个是运行C++静态构造方法,libc调用_objc_initdyld调用我们的静态构造函数之前,因此我们需要自己来调用一遍。

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

从方法名字就可以知道,这是一个运行时的初始化方法,包含了未依赖的分类初始化已经申请内存类的初始化

五、exception_init()
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}

这个初始化,从命名来看就知道是异常的初始化,注释也说明了,这个是初始化的libobjc的异常处理系统,将会在map_images()方法调用。
这里在set_terminate方法中传入的是_objc_terminate方法,全局搜索一下:

/***********************************************************************
* _objc_terminate
* Custom std::terminate handler.
*
* The uncaught exception callback is implemented as a std::terminate handler. 
* 1. Check if there's an active exception
* 2. If so, check if it's an Objective-C exception
* 3. If so, call our registered callback with the object.
* 4. Finally, call the previous terminate handler.
**********************************************************************/
static void (*old_terminate)(void) = nil;
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)();
        }
    }
}

通过注释可以知道,这个方法就是自定义的std::terminate handler,异常处理操作流程:
1、检测是否存在一个当前活跃的异常;
2、如果活跃检测是否是一个Objc-C的异常,因为可能还有C/C++等等;
3、如果是的,将调用Foundation库的异常处理;
4、最终,调用就的处理,也就是C/C++的异常处理。

六、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
}

缓存的初始化。

七、_imp_implementationWithBlock_init()
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
    // 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
}

imp的函数实现初始化,可以知道这里是懒加载形式实现的objc-trampolines库的初始化。

八、_dyld_objc_notify_register(&map_images, load_images, unmap_image)

这个方法是无法在objc中找到源码,根据11、APP的加载与dyld中的研究可以推测,是在dyld中实现,在dyld中找到源码:

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

void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}

分别找到的是头文件声明和cpp文件的实现,根据注释得到的信息:
1、这个主要是为objc runtime用的
2、注册操作会在objc镜像被映射、解除映射和初始化时被调用。
3、dyld会以一组包含objc-image-info的信息段的镜像为参数回调映射方法。
4、后面就是一些回调方法调用时机的解释。
这里对应了有三个参数:
mappedinitumapped对应传入的分别是&map_imagesload_imageunmap_image
在方法实现中,调用了dyld::registerObjCNotifiers方法:

void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    sNotifyObjCInit     = init;
    sNotifyObjCUnmapped = unmapped;

    // call 'mapped' function with all images mapped so far
    try {
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    }
    catch (const char* msg) {
        // ignore request to abort during registration
    }

    // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
    for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
        ImageLoader* image = *it;
        if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
            //调用sNotifyObjCInit
            (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
        }
    }
}

可以知道对应赋值的是sNotifyObjCMappedsNotifyObjCInitsNotifyObjCUnmapped
在上一篇文章11、APP的加载与dyld中分析到的是sNotifyObjCInit函数指针的调用流程,是通过notifySingle方法中调用的registerObjCNotifiers,最后才调用sNotifyObjCInit函数指针。

再分析一下sNotifyObjCMapped方法的调用,全局搜索:

static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
    std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sBatchHandlers);
    if ( (handlers != NULL) || ((state == dyld_image_state_bound) && (sNotifyObjCMapped != NULL)) ) {
        // don't use a vector because it will use malloc/free and we want notifcation to be low cost
        allImagesLock();
        dyld_image_info infos[allImagesCount()+1];
        ImageLoader* images[allImagesCount()+1];
        ImageLoader** end = images;
        for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
            dyld_image_states imageState = (*it)->getState();
            if ( (imageState == state) || (orLater && (imageState > state)) )
                *end++ = *it;
        }
        if ( sBundleBeingLoaded != NULL ) {
            dyld_image_states imageState = sBundleBeingLoaded->getState();
            if ( (imageState == state) || (orLater && (imageState > state)) )
                *end++ = sBundleBeingLoaded;
        }
        const char* dontLoadReason = NULL;
        uint32_t imageCount = (uint32_t)(end-images);
        if ( imageCount != 0 ) {
            // sort bottom up
            qsort(images, imageCount, sizeof(ImageLoader*), &imageSorter);

            const mach_header* mhs[imageCount];
            const char*        paths[imageCount];
            uint32_t           bulkNotifyImageCount = 0;

            // build info array
            for (unsigned int i=0; i < imageCount; ++i) {
                dyld_image_info* p = &infos[i];
                ImageLoader* image = images[i];
                //dyld::log("  state=%d, name=%s\n", state, image->getPath());
                p->imageLoadAddress = image->machHeader();
                p->imageFilePath    = image->getRealPath();
                p->imageFileModDate = image->lastModified();
                // get these registered with the kernel as early as possible
                if ( state == dyld_image_state_dependents_mapped)
                    notifyKernel(*image, true);
                // special case for add_image hook
                if ( state == dyld_image_state_bound ) {
                    if ( notifyAddImageCallbacks(image) ) {
                        // Add this to the list of images to bulk notify
                        mhs[bulkNotifyImageCount]   = infos[i].imageLoadAddress;
                        paths[bulkNotifyImageCount] = infos[i].imageFilePath;
                        ++bulkNotifyImageCount;
                    }
                }
            }

            if ( (state == dyld_image_state_bound) && !sAddBulkLoadImageCallbacks.empty() && (bulkNotifyImageCount != 0) ) {
                for (LoadImageBulkCallback func : sAddBulkLoadImageCallbacks) {
                    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)mhs[0], (uint64_t)func, 0);
                    (*func)(bulkNotifyImageCount, mhs, paths);
                }
            }
        }
#if SUPPORT_ACCELERATE_TABLES
        if ( sAllCacheImagesProxy != NULL ) {
            unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(state, orLater, &infos[imageCount]);
            // support _dyld_register_func_for_add_image()
            if ( state == dyld_image_state_bound ) {
                for (ImageCallback callback : sAddImageCallbacks) {
                    for (unsigned i=0; i < cacheCount; ++i) {
                        dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*callback), 0);
                        (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                    }
                }
                for (LoadImageCallback func : sAddLoadImageCallbacks) {
                    for (unsigned i=0; i < cacheCount; ++i) {
                        dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*func), 0);
                        (*func)(infos[imageCount+i].imageLoadAddress, infos[imageCount+i].imageFilePath, false);
                    }
                }
                if ( !sAddBulkLoadImageCallbacks.empty() ) {
                    const mach_header* bulk_mhs[cacheCount];
                    const char*        bulk_paths[cacheCount];
                    for (int i=0; i < cacheCount; ++i) {
                        bulk_mhs[i]   = infos[imageCount+i].imageLoadAddress;
                        bulk_paths[i] = infos[imageCount+i].imageFilePath;
                    }
                    for (LoadImageBulkCallback func : sAddBulkLoadImageCallbacks) {
                        dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)bulk_mhs[0], (uint64_t)func, 0);
                        (*func)(cacheCount, bulk_mhs, bulk_paths);
                    }
                }
            }
            imageCount += cacheCount;
        }
#endif
        if ( imageCount != 0 ) {
            if ( !onlyObjCMappedNotification ) {
                if ( onlyHandler != NULL ) {
                    const char* result = NULL;
                    if ( result == NULL ) {
                        result = (*onlyHandler)(state, imageCount, infos);
                    }
                    if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) {
                        //fprintf(stderr, "  images rejected by handler=%p\n", onlyHandler);
                        // make copy of thrown string so that later catch clauses can free it
                        dontLoadReason = strdup(result);
                    }
                }
                else {
                    // call each handler with whole array
                    if ( handlers != NULL ) {
                        for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
                            const char* result = (*it)(state, imageCount, infos);
                            if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) {
                                //fprintf(stderr, "  images rejected by handler=%p\n", *it);
                                // make copy of thrown string so that later catch clauses can free it
                                dontLoadReason = strdup(result);
                                break;
                            }
                        }
                    }
                }
            }
            // tell objc about new images
            if ( (onlyHandler == NULL) && ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) && (sNotifyObjCMapped != NULL) ) {
                const char* paths[imageCount];
                const mach_header* mhs[imageCount];
                unsigned objcImageCount = 0;
                for (int i=0; i < imageCount; ++i) {
                    ImageLoader* image = findImageByMachHeader(infos[i].imageLoadAddress);
                    bool hasObjC = false;
                    if ( image != NULL ) {
                        if ( image->objCMappedNotified() )
                            continue;
                        hasObjC = image->notifyObjC();
                    }
#if SUPPORT_ACCELERATE_TABLES
                    else if ( sAllCacheImagesProxy != NULL ) {
                        const mach_header* mh;
                        const char* path;
                        unsigned index;
                        if ( sAllCacheImagesProxy->addressInCache(infos[i].imageLoadAddress, &mh, &path, &index) ) {
                            hasObjC = (mh->flags & MH_HAS_OBJC);
                        }
                    }
#endif
                    if ( hasObjC ) {
                        paths[objcImageCount] = infos[i].imageFilePath;
                        mhs[objcImageCount]   = infos[i].imageLoadAddress;
                        ++objcImageCount;
                        if ( image != NULL )
                            image->setObjCMappedNotified();
                    }
                }
                if ( objcImageCount != 0 ) {
                    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
                    uint64_t t0 = mach_absolute_time();
                    //调用sNotifyObjCMapped
                    (*sNotifyObjCMapped)(objcImageCount, paths, mhs);
                    uint64_t t1 = mach_absolute_time();
                    ImageLoader::fgTotalObjCSetupTime += (t1-t0);
                }
            }
        }
        allImagesUnlock();
        if ( dontLoadReason != NULL )
            throw dontLoadReason;
        if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) {
            const struct mach_header* loadAddresses[imageCount];
            const char* loadPaths[imageCount];
            for(uint32_t i = 0; i<imageCount; ++i) {
                loadAddresses[i] = infos[i].imageLoadAddress;
                loadPaths[i] = infos[i].imageFilePath;
            }
            notifyMonitoringDyld(false, imageCount, loadAddresses, loadPaths);
        }
    }
}

可以知道是在notifyBatchPartial方法中调用的sNotifyObjCMapped,对照registerObjCNotifiers方法,可以知道与objc关联的流程是先对镜像进行映射,然后在进行初始化。


总结一下:
1、dyld流程中在通过调用libobjc库中的 _objc_init方法中的_dyld_objc_notify_register,最终调用dyld::registerObjCNotifiers方法。
2、在调用_dyld_objc_notify_register前,_objc_init方法实现了多个初始化方法,分别初始化了:
1.环境
2.线程缓存
3.静态构造方法
4.类和分类
5.OC异常处理
6.方法缓存
7.objc-trampolines库
3、registerObjCNotifiers方法中先绑定了sNotifyObjCMappedsNotifyObjCInitsNotifyObjCUnmapped三个函数指针,然后就通过调用notifyBatchPartial调用了sNotifyObjCMapped镜像映射方法。
4、registerObjCNotifiers最后才调用了sNotifyObjCInit方法,进行镜像的初始化。

相关文章

  • dyld与objc的关联

    在本篇文章中,主要探索dyld与objc是如何关联的。 objc_init _objc_init通过dyld的消息...

  • OC底层原理11—dyld与objc的关联

    本文介绍dyld与objc的关联 按照苹果的解释,dyld加载库时,libSystem会调用_objc_init,...

  • 12、dyld与objc的关联

    环境说明:objc-818.2源码、MacOS 11.2.1系统,Xcode 12.4,调试可以参考objc-ru...

  • dyld与objc的关联

    _objc_init 源码解析 根据源码所知,主要分为以下几部分: environ_init:初始化一系列环境变量...

  • dyld与objc的关联

    在上篇文章 dyld 了流程分析[https://www.jianshu.com/p/b2344b308c14] ...

  • dyld与objc的关联

    _objc_init 源码解析 environ_init方法:环境变量初始化 这些环境变量,均可以通过target...

  • 底层原理:懒加载类与非懒加载类

    上一篇文章我们分析了dyld跟objc的关联中,已经研究到了_dyld_objc_notify_register中...

  • dyld & objc的关联

    抛出问题 上回说到iOS应用程序加载大致流程分析[https://www.jianshu.com/p/ba53fa...

  • iOS - dyld与objc的关联

    本文的主要目的是理解dyld与objc是如何关联的 _objc_init 源码解析 首先,来看下libObjc中的...

  • 十二、dyld 和 Objc 的关联

    主要内容:一、_objc_init 源码分析二、dyld 和 Objc 的关联 一、_objc_init 源码分析...

网友评论

      本文标题:12、dyld与objc的关联

      本文链接:https://www.haomeiwen.com/subject/rxkqgktx.html