第十篇:IOS里map_images和load_images方法

2022-05-18  本文已影响0人  坚持才会看到希望

程序启动后会调用_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();  -- tls是线程的局部存储
    static_init(); -- 运行c++的一些静态函数
    runtime_init();
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

首先在我们Xcode里设置环境变量也就是打开load方法的输出,我们设置为OBJC_PRINT_LOAD_METHODS,然后看下运行代码后打印的。

WechatIMG1967.jpeg

objc[29649]: LOAD: class 'NSNotificationCenter' scheduled for +load
objc[29649]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[29649]: LOAD: +[NSNotificationCenter load]

objc[29649]: LOAD: +[NSObject(NSObject) load]

objc[29649]: LOAD: class 'NSColor' scheduled for +load
objc[29649]: LOAD: class 'NSApplication' scheduled for +load
objc[29649]: LOAD: class 'NSBinder' scheduled for +load
objc[29649]: LOAD: class 'NSColorSpaceColor' scheduled for +load
objc[29649]: LOAD: class 'NSNextStepFrame' scheduled for +load
objc[29649]: LOAD: +[NSColor load]

objc[29649]: LOAD: +[NSApplication load]

objc[29649]: LOAD: +[NSBinder load]

objc[29649]: LOAD: +[NSColorSpaceColor load]

objc[29649]: LOAD: +[NSNextStepFrame load]

objc[29649]: LOAD: class '_DKEventQuery' scheduled for +load
objc[29649]: LOAD: +[_DKEventQuery load]

在控制台里我们看到有好多load方法的打印,可以看到好多类实现了load方法。

下面是设置动态库的打印,设置环境变量为OBJC_PRINT_IMAGES,打印如下

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/GLKit.framework/Versions/A/GLKit (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/ModelIO.framework/Versions/A/ModelIO (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/PDFKit.framework/Versions/A/PDFKit (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/AVKit.framework/Versions/A/AVKit (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/SceneKit.framework/Versions/A/SceneKit (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory (has class properties) (preoptimized)

objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji (has class properties) (preoptimized)

下面是一些xcode部分环境变量设置,在源码里开启help_帮助就可以打印出:

objc[29649]: Objective-C runtime debugging. Set variable=YES to enable.
objc[29649]: OBJC_HELP: describe available environment variables
objc[29649]: OBJC_HELP is set
objc[29649]: OBJC_PRINT_OPTIONS: list which options are set
objc[29649]: OBJC_PRINT_OPTIONS is set
objc[29649]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[29649]: OBJC_PRINT_IMAGES is set
objc[29649]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[29649]: OBJC_PRINT_IMAGE_TIMES is set
objc[29649]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[29649]: OBJC_PRINT_LOAD_METHODS is set
objc[29649]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[29649]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[29649]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[29649]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[29649]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[29649]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[29649]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods

下面我们重点看下 _dyld_objc_notify_register(&map_images, load_images, unmap_image);这个方法里的map_images和load_images。&map_images是引用传递,load_images是值传递。

load_images方法就是去查找和调用load方法。load方法的调用顺序是 先找分类-->本类(子类)-->分类,如果两个分类都实现load方法,那就看编译的顺序,先编译就后执行

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    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);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

接着我们看map_images这个方法:

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

oid 
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__
                // If dyld3 optimized the main executable, then there shouldn't
                // be any selrefs needed in the dynamic map so we can just init
                // to a 0 sized map
                if ( !hi->hasPreoptimizedSelectors() ) {
                  size_t count;
                  _getObjc2SelectorRefs(hi, &count);
                  selrefCount += count;
                  _getObjc2MessageRefs(hi, &count);
                  selrefCount += count;
                }
#else
                _getObjcSelectorRefs(hi, &selrefCount);
#endif
                

其实在map_images这个方法里我们主要研究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++

    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;
            }
        }
上一篇下一篇

猜你喜欢

热点阅读