美文网首页iOS底层探索
iOS底层探索之_objc_init

iOS底层探索之_objc_init

作者: loongod | 来源:发表于2020-10-17 17:54 被阅读0次

一、_objc_init源码

_objc_init 也是在 libObjc 的源码中

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();
    // runtime运行时环境初始化
    runtime_init();
    // libobjc异常处理系统初始化
    exception_init();
    // 缓存条件初始化
    cache_init();
    // 启动回调机制。通常不会做什么,因为所有的初始化都是惰性的
    _imp_implementationWithBlock_init();
     /*
     _dyld_objc_notify_register -- dyld 注册的地方
     - 仅供objc运行时使用
     - 注册处理程序,以便在映射、取消映射 和初始化objc镜像文件时使用,dyld将使用包含objc_image_info的镜像文件数组,回调 mapped 函数
     
     map_images:dyld将image镜像文件加载进内存时,会触发该函数
     load_images:dyld初始化image会触发该函数
     unmap_image:dyld将image移除时会触发该函数
     */
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

_objc_init 这个方法里面基本上本身没有什么逻辑,又把逻辑封装了一下,下面看下里面每个方法的作用

二、environ_init() 环境变量初始化

  • environ_init
  • Read environment variables that affect the runtime.
  • Also print environment variable help, if requested.
    【译】
  • 读取影响运行时的环境变量。
  • 如果需要,还可以打印环境变量帮助。
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 循环拿出来放到外面,把判断条件去掉

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
        const option_t *opt = &Settings[i];
        _objc_inform("%s: %s", opt->env, opt->help);
        _objc_inform("%s is set", opt->env);
    }

运行工程,会在控制台输出如下环境变量帮助:

objc[15188]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[15188]: OBJC_PRINT_IMAGES is set
objc[15188]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[15188]: OBJC_PRINT_IMAGE_TIMES is set
objc[15188]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[15188]: OBJC_PRINT_LOAD_METHODS is set
objc[15188]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[15188]: OBJC_PRINT_INITIALIZE_METHODS is set
objc[15188]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[15188]: OBJC_PRINT_RESOLVED_METHODS is set
objc[15188]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[15188]: OBJC_PRINT_CLASS_SETUP is set
objc[15188]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[15188]: OBJC_PRINT_PROTOCOL_SETUP is set
objc[15188]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[15188]: OBJC_PRINT_IVAR_SETUP is set
objc[15188]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[15188]: OBJC_PRINT_VTABLE_SETUP is set
objc[15188]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[15188]: OBJC_PRINT_VTABLE_IMAGES is set
objc[15188]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[15188]: OBJC_PRINT_CACHE_SETUP is set
objc[15188]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[15188]: OBJC_PRINT_FUTURE_CLASSES is set
objc[15188]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[15188]: OBJC_PRINT_PREOPTIMIZATION is set
objc[15188]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[15188]: OBJC_PRINT_CXX_CTORS is set
objc[15188]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[15188]: OBJC_PRINT_EXCEPTIONS is set
objc[15188]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[15188]: OBJC_PRINT_EXCEPTION_THROW is set
objc[15188]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[15188]: OBJC_PRINT_ALT_HANDLERS is set
objc[15188]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[15188]: OBJC_PRINT_REPLACED_METHODS is set
objc[15188]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[15188]: OBJC_PRINT_DEPRECATION_WARNINGS is set
objc[15188]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[15188]: OBJC_PRINT_POOL_HIGHWATER is set
objc[15188]: OBJC_PRINT_CUSTOM_CORE: log classes with custom core methods
objc[15188]: OBJC_PRINT_CUSTOM_CORE is set
objc[15188]: OBJC_PRINT_CUSTOM_RR: log classes with custom retain/release methods
objc[15188]: OBJC_PRINT_CUSTOM_RR is set
objc[15188]: OBJC_PRINT_CUSTOM_AWZ: log classes with custom allocWithZone methods
objc[15188]: OBJC_PRINT_CUSTOM_AWZ is set
objc[15188]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[15188]: OBJC_PRINT_RAW_ISA is set
objc[15188]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[15188]: OBJC_DEBUG_UNLOAD is set
objc[15188]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[15188]: OBJC_DEBUG_FRAGILE_SUPERCLASSES is set
objc[15188]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[15188]: OBJC_DEBUG_NIL_SYNC is set
objc[15188]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[15188]: OBJC_DEBUG_NONFRAGILE_IVARS is set
objc[15188]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[15188]: OBJC_DEBUG_ALT_HANDLERS is set
objc[15188]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[15188]: OBJC_DEBUG_MISSING_POOLS is set
objc[15188]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[15188]: OBJC_DEBUG_POOL_ALLOCATION is set
objc[15188]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[15188]: OBJC_DEBUG_DUPLICATE_CLASSES is set
objc[15188]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[15188]: OBJC_DEBUG_DONT_CRASH is set
objc[15188]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[15188]: OBJC_DISABLE_VTABLES is set
objc[15188]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[15188]: OBJC_DISABLE_PREOPTIMIZATION is set
objc[15188]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[15188]: OBJC_DISABLE_TAGGED_POINTERS is set
objc[15188]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[15188]: OBJC_DISABLE_TAG_OBFUSCATION is set
objc[15188]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[15188]: OBJC_DISABLE_NONPOINTER_ISA is set
objc[15188]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[15188]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY is set

找一个 环境变量 OBJC_PRINT_LOAD_METHODS : log calls to class and category +load methods,记录对类和类别 + load 方法的调用

Edit Scheme 设置

设置环境变量.png

在自定义类中,添加 + load 方法

@implementation GLPerson

+ (void)load {}
@end

运行工程:

类的load方法调用.png

会发现,不管是系统的,还是自定义的类,所有实现 + load 方法的类都会打印出来

通过终端命令 export OBJC_HELP=1 也可以查看环境变量帮助

终端打印OBJC_HELP.png

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

四、static_init 运行C++静态构造函数

  • 运行 C ++ 静态构造函数。
  • libcdyld 调用我们的静态构造函数之前调用_objc_init(),因此,我们必须自己做。
static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
}

五、runtime_init runtime运行时环境初始化

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

六、exception_init 异常处理系统初始化

初始化 libobjc 的异常处理系统。由map_images() 调用

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

七、cache_init 缓存条件初始化

void cache_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 启动回调机制

该方法主要是启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载 libobjc-trampolines.dylib

void
_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
}

九、_dyld_objc_notify_register 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);

这个方法本身只是一个注册函数的声明,在 objc 源码中找不到实现了,具体实现还需去 dyld 源码中去查看。

_dyld_objc_notify_register(&map_images, load_images, unmap_image); 传了3个参数:

  1. map_images : 引用传递,dyldimage(镜像文件)加载进内存时,会触发该函数;
  2. load_images : 值传递,dyld 初始化 image 会触发该函数;
  3. unmap_image : 值传递,dyldimage 移除时,会触发该函数;
dyld_objc.png

相关文章

网友评论

    本文标题:iOS底层探索之_objc_init

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