zoukankan      html  css  js  c++  java
  • OC 底层探索 13、类的加载1

    本文开始探索类的加载。调试源码 objc 源码工程

    _objc_init 函数:

     1 /***********************************************************************
     2 * _objc_init
     3 * Bootstrap initialization. Registers our image notifier with dyld.
     4 * Called by libSystem BEFORE library initialization time
     5 **********************************************************************/
     6 
     7 void _objc_init(void)
     8 {
     9     static bool initialized = false;
    10     if (initialized) return;
    11     initialized = true;
    12     
    13     // fixme defer initialization until an objc-using image is found?
    14     environ_init();
    15     tls_init();
    16     static_init();
    17     runtime_init();
    18     exception_init();
    19     cache_init();
    20     _imp_implementationWithBlock_init();
    21 
    22     _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    23 
    24 #if __OBJC2__
    25     didCallDyldNotifyRegister = true;
    26 #endif
    27 }

    一、各 init 初始化的简单了解

    1)环境变量的初始化与简单使用

    我们从 14 行 environ_init() 环境变量初始化开始进行探究。

    environ_init() 函数:

    读取影响运行时的环境变量

     1 /***********************************************************************
     2 * environ_init
     3 * Read environment variables that affect the runtime.
     4 * Also print environment variable help, if requested.
     5 **********************************************************************/
     6 void environ_init(void) 
     7 {
     8     if (issetugid()) {
     9         // All environment variables are silently ignored when setuid or setgid
    10         // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
    11         return;
    12     } 
    13 
    14     bool PrintHelp = false;
    15     bool PrintOptions = false;
    16     bool maybeMallocDebugging = false;
    17 
    18     // Scan environ[] directly instead of calling getenv() a lot.
    19     // This optimizes the case where none are set.
    20     for (char **p = *_NSGetEnviron(); *p != nil; p++) {
    21             ...... // 代码不全部展示了 
    22     }
    23 
    24     // Special case: enable some autorelease pool debugging 
    25     // when some malloc debugging is enabled 
    26     // and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
    27     if (maybeMallocDebugging) {
    28         ...... // 代码不全部展示了
    29     }
    30 
    31     // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
    32     if (PrintHelp  ||  PrintOptions) {
    33         if (PrintHelp) {
    34             _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
    35             _objc_inform("OBJC_HELP: describe available environment variables");
    36             if (PrintOptions) {
    37                 _objc_inform("OBJC_HELP is set");
    38             }
    39             _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
    40         }
    41         if (PrintOptions) {
    42             _objc_inform("OBJC_PRINT_OPTIONS is set");
    43         }
    44 
    45         for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
    46             const option_t *opt = &Settings[i];            
    47             if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
    48             if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
    49         }
    50     }
    51     
    52     // 我加的调试代码
    53     for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
    54         const option_t *opt = &Settings[i];
    55         _objc_inform("%s: %s", opt->env, opt->help);
    56         _objc_inform("%s is set", opt->env);
    57     }
    58 }

    我们通过运行工程(objc源码工程),发现上述代码都没有走进去,把打印输出提取出来(52行)再次运行,可看到输出信息如下(信息比较长截取部分):

    环境变量的使用

    上图输出结果倒数4行,我们看到是 OBJC_DISABLE_NONPOINTER_ISA 是 isa 的设置,我们在工程中 Edit Scheme 中添加此属性,如下图:

     

    main.m 中创建一个 MYPerson,不同场景运行工程结果如下:

    我们可通过环境换量的设置,更便捷的对工程有整体的了解。

    更直观的,我们添加 OBJC_PRINT_LOAD_METHODS : YES

    注释掉上述调试代码(52~57),再次运行,所有的 load 方法都输出了,如下:

    2)tls_init() -

    关于线程 key 的绑定 - 例如现成的析构函数。(runloop/自动释放池等都依赖于线程)

    1 void tls_init(void)
    2 {
    3 #if SUPPORT_DIRECT_THREAD_KEYS
    4     pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
    5 #else
    6     _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
    7 #endif
    8 }

    static_init() - objc 中的静态构造函数

    运行C++静态构造函数,它们的调用会比 dyld 还早。

     1 /***********************************************************************
     2 * static_init
     3 * Run C++ static constructor functions.
     4 * libc calls _objc_init() before dyld would call our static constructors, 
     5 * so we have to do it ourselves.
     6 **********************************************************************/
     7 static void static_init()
     8 {
     9     size_t count;
    10     auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    11     for (size_t i = 0; i < count; i++) {
    12         inits[i]();
    13     }
    14 }

    runtime_init() 

    runtime 运行时环境初始化

    1 void runtime_init(void)
    2 {
    3     objc::unattachedCategories.init(32);// 分类处理的初始化
    4     objc::allocatedClasses.init();// 表 用来储存加载的类
    5 }

    exception_init() - 异常

    初始化libobjc 的异常处理系统。

    crash 系统抛出异常,程序中断

    1 /***********************************************************************
    2 * exception_init
    3 * Initialize libobjc's exception handling system.
    4 * Called by map_images().
    5 **********************************************************************/
    6 void exception_init(void)
    7 {
    8     old_terminate = std::set_terminate(&_objc_terminate);
    9 }
     1 /***********************************************************************
     2 * _objc_terminate
     3 * Custom std::terminate handler.
     4 *
     5 * The uncaught exception callback is implemented as a std::terminate handler. 
     6 * 1. Check if there's an active exception
     7 * 2. If so, check if it's an Objective-C exception
     8 * 3. If so, call our registered callback with the object.
     9 * 4. Finally, call the previous terminate handler.
    10 **********************************************************************/
    11 static void (*old_terminate)(void) = nil;
    12 static void _objc_terminate(void)
    13 {
    14     if (PrintExceptions) {
    15         _objc_inform("EXCEPTIONS: terminating");
    16     }
    17 
    18     if (! __cxa_current_exception_type()) {
    19         // No current exception.
    20         (*old_terminate)();
    21     }
    22     else {
    23         // There is a current exception. Check if it's an objc exception.
    24         @try {
    25             __cxa_rethrow();
    26         } @catch (id e) {
    27             // It's an objc object. Call Foundation's handler, if any. 句柄 函数回调 - 截取异常
    28             (*uncaught_handler)((id)e);
    29             (*old_terminate)();
    30         } @catch (...) {
    31             // It's not an objc object. Continue to C++ terminate.
    32             (*old_terminate)();
    33         }
    34     }
    35 }

    cache_init()

    缓存条件初始化

     1 void cache_init()
     2 {
     3 #if HAVE_TASK_RESTARTABLE_RANGES
     4     mach_msg_type_number_t count = 0;
     5     kern_return_t kr;
     6 
     7     while (objc_restartableRanges[count].location) {
     8         count++;
     9     }
    10 
    11     kr = task_restartable_ranges_register(mach_task_self(),
    12                                           objc_restartableRanges, count);
    13     if (kr == KERN_SUCCESS) return;
    14     _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
    15                 kr, mach_error_string(kr));
    16 #endif // HAVE_TASK_RESTARTABLE_RANGES
    17 }

    _imp_implementationWithBlock_init()

    启用回调机制。通常情况下不做什么操作,因为所有的初始化都是懒加载的,但对于某些进程,迫切的需要加载 trampolines dylib.

     1 /// Initialize the trampoline machinery. Normally this does nothing, as
     2 /// everything is initialized lazily, but for certain processes we eagerly load
     3 /// the trampolines dylib.
     4 void
     5 _imp_implementationWithBlock_init(void)
     6 {
     7 #if TARGET_OS_OSX
     8     // Eagerly load libobjc-trampolines.dylib in certain processes. Some
     9     // programs (most notably QtWebEngineProcess used by older versions of
    10     // embedded Chromium) enable a highly restrictive sandbox profile which
    11     // blocks access to that dylib. If anything calls
    12     // imp_implementationWithBlock (as AppKit has started doing) then we'll
    13     // crash trying to load it. Loading it here sets it up before the sandbox
    14     // profile is enabled and blocks it.
    15     //
    16     // This fixes EA Origin (rdar://problem/50813789)
    17     // and Steam (rdar://problem/55286131)
    18     if (__progname &&
    19         (strcmp(__progname, "QtWebEngineProcess") == 0 ||
    20          strcmp(__progname, "Steam Helper") == 0)) {
    21         Trampolines.Initialize();
    22     }
    23 #endif
    24 }

    二、_dyld_objc_notify_register() - 

     

    代码 --> 编译 --> machO --> 内存

    把 macho 各式各样数据加载到内存中,如何加载?--> map_images 镜像文件装载到内存 

    _dyld_objc_notify_register() 是 dyld 库 的调用(跨库,实现在dyld库中)。

    执行流程:dyld 中 从 dyld_start 开始 --> 一系列操作 --> 跳 objc 中 处理完成 --> 回到 dyld 中继续执行 --> main 入口。 具体流程可见 OC 底层探索 12.

     1 /**
     2     mapped -- &map_images
     3     init -- load_images
     4 */
     5 void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
     6                                 _dyld_objc_notify_init      init,
     7                                 _dyld_objc_notify_unmapped  unmapped)
     8 {
     9     if ( gUseDyld3 )
    10         return dyld3::_dyld_objc_notify_register(mapped, init, unmapped);
    11 
    12     DYLD_LOCK_THIS_BLOCK;
    13     static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL;
    14 
    15     if(p == NULL)
    16         _dyld_func_lookup("__dyld_objc_notify_register", (void**)&p);
    17     p(mapped, init, unmapped);
    18 }

    map_images / load_images / unmap_image 何时调:

    dyld: 

     1 void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
     2 {
     3     // record functions to call
     4     sNotifyObjCMapped    = mapped;
     5     sNotifyObjCInit        = init;
     6     sNotifyObjCUnmapped = unmapped;
     7 
     8     // call 'mapped' function with all images mapped so far
     9     try {
    10         notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    11     }
    12     catch (const char* msg) {
    13         // ignore request to abort during registration
    14     }
    15 
    16     // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
    17     for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
    18         ImageLoader* image = *it;
    19         if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
    20             dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
    21             (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
    22         }
    23     }
    24 }

    sNotifyObjCMapped --> notifyBatchPartial() : (*sNotifyObjCMapped)(objcImageCount, paths, mhs);

    sNotifyObjCInit --> notifySingle() : (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());

    sNotifyObjCUnmapped --> removeImage() : (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());

    1)map_images 

     1 /***********************************************************************
     2 * map_images
     3 * Process the given images which are being mapped in by dyld.
     4 * Calls ABI-agnostic code after taking ABI-specific locks.
     5 *
     6 * Locking: write-locks runtimeLock
     7 **********************************************************************/
     8 void
     9 map_images(unsigned count, const char * const paths[],
    10            const struct mach_header * const mhdrs[])
    11 {
    12     mutex_locker_t lock(runtimeLock);
    13     return map_images_nolock(count, paths, mhdrs);
    14 }

    跳到 map_images_nolock() 实现: 

    _read_images():代码比较长

      1 /***********************************************************************
      2 * _read_images
      3 * Perform initial processing of the headers in the linked 
      4 * list beginning with headerList. 
      5 *
      6 * Called by: map_images_nolock
      7 *
      8 * Locking: runtimeLock acquired by map_images
      9 **********************************************************************/
     10 void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
     11 {
     12     header_info *hi;
     13     uint32_t hIndex;
     14     size_t count;
     15     size_t i;
     16     Class *resolvedFutureClasses = nil;
     17     size_t resolvedFutureClassCount = 0;
     18     static bool doneOnce;
     19     bool launchTime = NO;
     20     TimeLogger ts(PrintImageTimes);
     21 
     22     runtimeLock.assertLocked();
     23 
     24 #define EACH_HEADER 
     25     hIndex = 0;         
     26     hIndex < hCount && (hi = hList[hIndex]); 
     27     hIndex++
     28 
     29     if (!doneOnce) {
     30         doneOnce = YES;
     31         launchTime = YES;
     32 
     33 #if SUPPORT_NONPOINTER_ISA
     34         // Disable non-pointer isa under some conditions.
     35 
     36 # if SUPPORT_INDEXED_ISA
     37         // Disable nonpointer isa if any image contains old Swift code
     38         for (EACH_HEADER) {
     39             if (hi->info()->containsSwift()  &&
     40                 hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
     41             {
     42                 DisableNonpointerIsa = true;
     43                 if (PrintRawIsa) {
     44                     _objc_inform("RAW ISA: disabling non-pointer isa because "
     45                                  "the app or a framework contains Swift code "
     46                                  "older than Swift 3.0");
     47                 }
     48                 break;
     49             }
     50         }
     51 # endif
     52 
     53 # if TARGET_OS_OSX
     54         // Disable non-pointer isa if the app is too old
     55         // (linked before OS X 10.11)
     56         if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
     57             DisableNonpointerIsa = true;
     58             if (PrintRawIsa) {
     59                 _objc_inform("RAW ISA: disabling non-pointer isa because "
     60                              "the app is too old (SDK version " SDK_FORMAT ")",
     61                              FORMAT_SDK(dyld_get_program_sdk_version()));
     62             }
     63         }
     64 
     65         // Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
     66         // New apps that load old extensions may need this.
     67         for (EACH_HEADER) {
     68             if (hi->mhdr()->filetype != MH_EXECUTE) continue;
     69             unsigned long size;
     70             if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
     71                 DisableNonpointerIsa = true;
     72                 if (PrintRawIsa) {
     73                     _objc_inform("RAW ISA: disabling non-pointer isa because "
     74                                  "the app has a __DATA,__objc_rawisa section");
     75                 }
     76             }
     77             break;  // assume only one MH_EXECUTE image
     78         }
     79 # endif
     80 
     81 #endif
     82 
     83         if (DisableTaggedPointers) {
     84             disableTaggedPointers();
     85         }
     86         
     87         initializeTaggedPointerObfuscator();
     88 
     89         if (PrintConnecting) {
     90             _objc_inform("CLASS: found %d classes during launch", totalClasses);
     91         }
     92 
     93         // namedClasses
     94         // Preoptimized classes don't go in this table.
     95         // 4/3 is NXMapTable's load factor
     96         int namedClassesSize = 
     97             (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
     98         gdb_objc_realized_classes =
     99             NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    100 
    101         ts.log("IMAGE TIMES: first time tasks");
    102     }
    103 
    104     // Fix up @selector references
    105     static size_t UnfixedSelectors;
    106     {
    107         mutex_locker_t lock(selLock);
    108         for (EACH_HEADER) {
    109             if (hi->hasPreoptimizedSelectors()) continue;
    110 
    111             bool isBundle = hi->isBundle();
    112             SEL *sels = _getObjc2SelectorRefs(hi, &count);
    113             UnfixedSelectors += count;
    114             for (i = 0; i < count; i++) {
    115                 const char *name = sel_cname(sels[i]);
    116                 SEL sel = sel_registerNameNoLock(name, isBundle);
    117                 if (sels[i] != sel) {
    118                     sels[i] = sel;
    119                 }
    120             }
    121         }
    122     }
    123 
    124     ts.log("IMAGE TIMES: fix up selector references");
    125 
    126     // Discover classes. Fix up unresolved future classes. Mark bundle classes.
    127     bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
    128 
    129     for (EACH_HEADER) {
    130         if (! mustReadClasses(hi, hasDyldRoots)) {
    131             // Image is sufficiently optimized that we need not call readClass()
    132             continue;
    133         }
    134 
    135         classref_t const *classlist = _getObjc2ClassList(hi, &count);
    136 
    137         bool headerIsBundle = hi->isBundle();
    138         bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
    139 
    140         for (i = 0; i < count; i++) {
    141             Class cls = (Class)classlist[i];
    142             Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    143 
    144             if (newCls != cls  &&  newCls) {
    145                 // Class was moved but not deleted. Currently this occurs 
    146                 // only when the new class resolved a future class.
    147                 // Non-lazily realize the class below.
    148                 resolvedFutureClasses = (Class *)
    149                     realloc(resolvedFutureClasses, 
    150                             (resolvedFutureClassCount+1) * sizeof(Class));
    151                 resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
    152             }
    153         }
    154     }
    155 
    156     ts.log("IMAGE TIMES: discover classes");
    157 
    158     // Fix up remapped classes
    159     // Class list and nonlazy class list remain unremapped.
    160     // Class refs and super refs are remapped for message dispatching.
    161     
    162     if (!noClassesRemapped()) {
    163         for (EACH_HEADER) {
    164             Class *classrefs = _getObjc2ClassRefs(hi, &count);
    165             for (i = 0; i < count; i++) {
    166                 remapClassRef(&classrefs[i]);
    167             }
    168             // fixme why doesn't test future1 catch the absence of this?
    169             classrefs = _getObjc2SuperRefs(hi, &count);
    170             for (i = 0; i < count; i++) {
    171                 remapClassRef(&classrefs[i]);
    172             }
    173         }
    174     }
    175 
    176     ts.log("IMAGE TIMES: remap classes");
    177 
    178 #if SUPPORT_FIXUP
    179     // Fix up old objc_msgSend_fixup call sites
    180     for (EACH_HEADER) {
    181         message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
    182         if (count == 0) continue;
    183 
    184         if (PrintVtables) {
    185             _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
    186                          "call sites in %s", count, hi->fname());
    187         }
    188         for (i = 0; i < count; i++) {
    189             fixupMessageRef(refs+i);
    190         }
    191     }
    192 
    193     ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    194 #endif
    195 
    196     bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
    197 
    198     // Discover protocols. Fix up protocol refs.
    199     for (EACH_HEADER) {
    200         extern objc_class OBJC_CLASS_$_Protocol;
    201         Class cls = (Class)&OBJC_CLASS_$_Protocol;
    202         ASSERT(cls);
    203         NXMapTable *protocol_map = protocols();
    204         bool isPreoptimized = hi->hasPreoptimizedProtocols();
    205 
    206         // Skip reading protocols if this is an image from the shared cache
    207         // and we support roots
    208         // Note, after launch we do need to walk the protocol as the protocol
    209         // in the shared cache is marked with isCanonical() and that may not
    210         // be true if some non-shared cache binary was chosen as the canonical
    211         // definition
    212         if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
    213             if (PrintProtocols) {
    214                 _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
    215                              hi->fname());
    216             }
    217             continue;
    218         }
    219 
    220         bool isBundle = hi->isBundle();
    221 
    222         protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    223         for (i = 0; i < count; i++) {
    224             readProtocol(protolist[i], cls, protocol_map, 
    225                          isPreoptimized, isBundle);
    226         }
    227     }
    228 
    229     ts.log("IMAGE TIMES: discover protocols");
    230 
    231     // Fix up @protocol references
    232     // Preoptimized images may have the right 
    233     // answer already but we don't know for sure.
    234     for (EACH_HEADER) {
    235         // At launch time, we know preoptimized image refs are pointing at the
    236         // shared cache definition of a protocol.  We can skip the check on
    237         // launch, but have to visit @protocol refs for shared cache images
    238         // loaded later.
    239         if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
    240             continue;
    241         protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
    242         for (i = 0; i < count; i++) {
    243             remapProtocolRef(&protolist[i]);
    244         }
    245     }
    246 
    247     ts.log("IMAGE TIMES: fix up @protocol references");
    248 
    249     // Discover categories. Only do this after the initial category
    250     // attachment has been done. For categories present at startup,
    251     // discovery is deferred until the first load_images call after
    252     // the call to _dyld_objc_notify_register completes. rdar://problem/53119145
    253     if (didInitialAttachCategories) {
    254         for (EACH_HEADER) {
    255             load_categories_nolock(hi);
    256         }
    257     }
    258 
    259     ts.log("IMAGE TIMES: discover categories");
    260 
    261     // Category discovery MUST BE Late to avoid potential races
    262     // when other threads call the new category code before
    263     // this thread finishes its fixups.
    264 
    265     // +load handled by prepare_load_methods()
    266 
    267     // Realize non-lazy classes (for +load methods and static instances)
    268     for (EACH_HEADER) {
    269         classref_t const *classlist = 
    270             _getObjc2NonlazyClassList(hi, &count);
    271         for (i = 0; i < count; i++) {
    272             Class cls = remapClass(classlist[i]);
    273             if (!cls) continue;
    274 
    275             addClassTableEntry(cls);
    276 
    277             if (cls->isSwiftStable()) {
    278                 if (cls->swiftMetadataInitializer()) {
    279                     _objc_fatal("Swift class %s with a metadata initializer "
    280                                 "is not allowed to be non-lazy",
    281                                 cls->nameForLogging());
    282                 }
    283                 // fixme also disallow relocatable classes
    284                 // We can't disallow all Swift classes because of
    285                 // classes like Swift.__EmptyArrayStorage
    286             }
    287             realizeClassWithoutSwift(cls, nil);
    288         }
    289     }
    290 
    291     ts.log("IMAGE TIMES: realize non-lazy classes");
    292 
    293     // Realize newly-resolved future classes, in case CF manipulates them
    294     if (resolvedFutureClasses) {
    295         for (i = 0; i < resolvedFutureClassCount; i++) {
    296             Class cls = resolvedFutureClasses[i];
    297             if (cls->isSwiftStable()) {
    298                 _objc_fatal("Swift class is not allowed to be future");
    299             }
    300             realizeClassWithoutSwift(cls, nil);
    301             cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
    302         }
    303         free(resolvedFutureClasses);
    304     }
    305 
    306     ts.log("IMAGE TIMES: realize future classes");
    307 
    308     if (DebugNonFragileIvars) {
    309         realizeAllClasses();
    310     }
    311 
    312 
    313     // Print preoptimization statistics
    314     if (PrintPreopt) {
    315         static unsigned int PreoptTotalMethodLists;
    316         static unsigned int PreoptOptimizedMethodLists;
    317         static unsigned int PreoptTotalClasses;
    318         static unsigned int PreoptOptimizedClasses;
    319 
    320         for (EACH_HEADER) {
    321             if (hi->hasPreoptimizedSelectors()) {
    322                 _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors "
    323                              "in %s", hi->fname());
    324             }
    325             else if (hi->info()->optimizedByDyld()) {
    326                 _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors "
    327                              "in %s", hi->fname());
    328             }
    329 
    330             classref_t const *classlist = _getObjc2ClassList(hi, &count);
    331             for (i = 0; i < count; i++) {
    332                 Class cls = remapClass(classlist[i]);
    333                 if (!cls) continue;
    334 
    335                 PreoptTotalClasses++;
    336                 if (hi->hasPreoptimizedClasses()) {
    337                     PreoptOptimizedClasses++;
    338                 }
    339                 
    340                 const method_list_t *mlist;
    341                 if ((mlist = ((class_ro_t *)cls->data())->baseMethods())) {
    342                     PreoptTotalMethodLists++;
    343                     if (mlist->isFixedUp()) {
    344                         PreoptOptimizedMethodLists++;
    345                     }
    346                 }
    347                 if ((mlist=((class_ro_t *)cls->ISA()->data())->baseMethods())) {
    348                     PreoptTotalMethodLists++;
    349                     if (mlist->isFixedUp()) {
    350                         PreoptOptimizedMethodLists++;
    351                     }
    352                 }
    353             }
    354         }
    355 
    356         _objc_inform("PREOPTIMIZATION: %zu selector references not "
    357                      "pre-optimized", UnfixedSelectors);
    358         _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",
    359                      PreoptOptimizedMethodLists, PreoptTotalMethodLists, 
    360                      PreoptTotalMethodLists
    361                      ? 100.0*PreoptOptimizedMethodLists/PreoptTotalMethodLists 
    362                      : 0.0);
    363         _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered",
    364                      PreoptOptimizedClasses, PreoptTotalClasses, 
    365                      PreoptTotalClasses 
    366                      ? 100.0*PreoptOptimizedClasses/PreoptTotalClasses
    367                      : 0.0);
    368         _objc_inform("PREOPTIMIZATION: %zu protocol references not "
    369                      "pre-optimized", UnfixedProtocolReferences);
    370     }
    371 
    372 #undef EACH_HEADER
    373 }

    read_images 做了什么,概括:

    1. 条件控制进行一次性的加载 - once
    2. 修复预编译阶段的 ‘@selector’ 的混乱问题
    3. 错误混乱的 类 的处理
    4. 修复 重映射 一些没有被镜像文件加载进来的类
    5. 修复一些消息
    6. 当类里面有协议时,readProtocol
    7. 修复没被加载的协议
    8. 分类的处理
    9. 非懒加载的类的加载处理
    10. 实现新解析的未来类,以防CF操作它们

    下面对 read_images 详细解析

    1、fix up @selector(上面源码的104行): 

    selector 并非简单的字符串,而是一串 带地址的字符串.

    2、Discover classes 类的处理(上面代码的126行开始)

    144行:Class was moved but not deleted - 出现类的混乱的场景比较少,代码暂时执行不进去。

    对于类,一般加载内存后不会移动,相对移动来说更倾向于删除重建,移动对性能能消耗太大。

    142行代码 readClass():

    readClass() 做了什么?.

    readClass() 的实现源码:

     1 /***********************************************************************
     2 * readClass
     3 * Read a class and metaclass as written by a compiler.
     4 * Returns the new class pointer. This could be: 
     5 * - cls
     6 * - nil  (cls has a missing weak-linked superclass)
     7 * - something else (space for this class was reserved by a future class)
     8 *
     9 * Note that all work performed by this function is preflighted by 
    10 * mustReadClasses(). Do not change this function without updating that one.
    11 *
    12 * Locking: runtimeLock acquired by map_images or objc_readClassPair
    13 **********************************************************************/
    14 Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    15 {
    16     const char *mangledName = cls->mangledName();
    17     
    18     if (missingWeakSuperclass(cls)) {
    19         // No superclass (probably weak-linked). 
    20         // Disavow any knowledge of this subclass.
    21         if (PrintConnecting) {
    22             _objc_inform("CLASS: IGNORING class '%s' with "
    23                          "missing weak-linked superclass", 
    24                          cls->nameForLogging());
    25         }
    26         addRemappedClass(cls, nil);
    27         cls->superclass = nil;
    28         return nil;
    29     }
    30     
    31     cls->fixupBackwardDeployingStableSwift();
    32 
    33     Class replacing = nil;
    34     if (Class newCls = popFutureNamedClass(mangledName)) {
    35         // This name was previously allocated as a future class.
    36         // Copy objc_class to future class's struct.
    37         // Preserve future's rw data block.
    38         
    39         if (newCls->isAnySwift()) {
    40             _objc_fatal("Can't complete future class request for '%s' "
    41                         "because the real class is too big.", 
    42                         cls->nameForLogging());
    43         }
    44         
    45         class_rw_t *rw = newCls->data();
    46         const class_ro_t *old_ro = rw->ro();
    47         memcpy(newCls, cls, sizeof(objc_class));
    48         rw->set_ro((class_ro_t *)newCls->data());
    49         newCls->setData(rw);
    50         freeIfMutable((char *)old_ro->name);
    51         free((void *)old_ro);
    52         
    53         addRemappedClass(cls, newCls);
    54         
    55         replacing = cls;
    56         cls = newCls;
    57     }
    58     
    59     if (headerIsPreoptimized  &&  !replacing) {
    60         // class list built in shared cache
    61         // fixme strict assert doesn't work because of duplicates
    62         // ASSERT(cls == getClass(name));
    63         ASSERT(getClassExceptSomeSwift(mangledName));
    64     } else {
    65         addNamedClass(cls, mangledName, replacing);
    66         addClassTableEntry(cls);
    67     }
    68 
    69     // for future reference: shared cache never contains MH_BUNDLEs
    70     if (headerIsBundle) {
    71         cls->data()->flags |= RO_FROM_BUNDLE;
    72         cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    73     }
    74     
    75     return cls;
    76 }

    这里因系统类较多,不好调试,我们添加自己的类进行探究:

     

     继续断点调试:上面代码34行的并未进去!

    走进了 65 行,即下图 3247 行:

    2.1)addNamedClass():--> name 加到 cls 里面

     1 /***********************************************************************
     2 * addNamedClass
     3 * Adds name => cls to the named non-meta class map.// 将 name=>cls 添加到费元类的映射
     4 * Warns about duplicate class names and keeps the old mapping.// 重复的类名将保留旧的映射
     5 * Locking: runtimeLock must be held by the caller
     6 **********************************************************************/
     7 static void addNamedClass(Class cls, const char *name, Class replacing = nil)
     8 {
     9     runtimeLock.assertLocked();
    10     Class old;
    11     if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
    12         inform_duplicate(name, old, cls);
    13 
    14         // getMaybeUnrealizedNonMetaClass uses name lookups.
    15         // Classes not found by name lookup must be in the
    16         // secondary meta->nonmeta table.
    17         addNonMetaClass(cls);
    18     } else {
    19         NXMapInsert(gdb_objc_realized_classes, name, cls);// 插入
    20     }
    21     ASSERT(!(cls->data()->flags & RO_META));
    22 
    23     // wrong: constructed classes are already realized when they get here
    24     // ASSERT(!cls->isRealized());
    25 }

    类名来自于 mangleName:--> 已经初始化or未来类则从 ro 里面读,否则从 machodata 里面读:

    2.2)addClassTableEntry(cls) 

     1 /***********************************************************************
     2 * addClassTableEntry
     3 * Add a class to the table of all classes. If addMeta is true,
     4 * automatically adds the metaclass of the class as well.
     5 * Locking: runtimeLock must be held by the caller.
     6 **********************************************************************/
     7 static void
     8 addClassTableEntry(Class cls, bool addMeta = true)
     9 {
    10     runtimeLock.assertLocked();
    11 
    12     // This class is allowed to be a known class via the shared cache or via
    13     // data segments, but it is not allowed to be in the dynamic table already.
    14     auto &set = objc::allocatedClasses.get();// 初始化位置在 runtime_init()
    15 
    16     ASSERT(set.find(cls) == set.end());
    17    
    18     if (!isKnownClass(cls))
    19         set.insert(cls);// 添加 insert
    20     if (addMeta)
    21         addClassTableEntry(cls->ISA(), false);// 元类就添加到元类
    22 }

    readClass 做了:将 cls 从 macho 的格式里面 读取添加到内存里面

    篇幅较长,接下篇:OC 底层探索 14、类的加载2

  • 相关阅读:
    实现Email传送
    用角色来管理权限
    最大在线人数统计
    解决DataGrid显示时间格式问题
    3秒后自动跳转
    ASP.NET页面的处理顺序
    乱码问题
    动态生成button并关联其onclick事件
    获取用户计算机信息
    鼠标滚轮缩放图片(js)
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13806192.html
Copyright © 2011-2022 走看看