oc写的应用的初始化过程

341 阅读5分钟

一、预热

应用程序加载过程会依赖很多的底层库,而底层库不应该每次都从0加载。

库是一种二进制的可执行代码。

  • 静态库
    • .a
    • .lib
  • 动态库:
    • .so
    • .framework
    • .dll

静态链接:链接阶段会将汇编生成的目标文件.o引用的(静态)库一起链接打包生成可执行文件的过程

动态链接:动态库在编译期间不会加载,在运行程序的时候再加载,共享内存,会被多次使用

编译过程

  1. 源文件 .h .m .cpp
  2. 预编译
  3. 编译
  4. 汇编
  5. 链接 - .a .lib .so
  6. 可执行文件

1、load方法在什么时候调用

2、子类和父类以及分类 load方法调用顺序是什么

3、子类和父类以及分类initialize方法调用顺序是什么

二、app的加载过程

1、app启动

  • dyld动态链接器 - 系统库通过dyld来管理 - 赋予静态库的运行时功能

    1. 加载libSystem

    2. 通过runtime注册回调函数

      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();
          lock_init();
          exception_init();
      
          _dyld_objc_notify_register(&map_images, load_images, unmap_image); // 这个
      }
      
      // -----------------------------------------------------
      
      // 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);
      
      • _dyld_objc_notify_mapped mapped加载镜像文件,返回成功失败,成为参数
      • initializers in that image. This is when objc calls any +load methods in that image.
    3. 加载新image

    4. 执行map_images、load_images ------imageLoader加载image-----> 返回 3. 循环直到加载完毕

    5. 调用main函数

2、map_images

  • 经过一些处理后进入_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses)

    1. 加载类 --- gdb_objc_realized --- class

      • initializeTaggedPointerObfuscator();

      • 创造一张哈希表

      • gdb_objc_realized_classes =
                    NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        // -----------
        // NX 是一种cpu的技术,分为rw和ro
        
      • // Discover classes. Fix up unresolved future classes. Mark bundle classes.

        发现有类,加载类有两种情况:1是已经加载过了,2是没有加载过,说明不是懒加载

        1. 已经加载过了:

          有重映射过程

        		for (EACH_HEADER) {
                classref_t *classlist = _getObjc2ClassList(hi, &count);
                
                if (! mustReadClasses(hi)) {
                    // Image is sufficiently optimized that we need not call readClass()
                    continue;
                }
        
                bool headerIsBundle = hi->isBundle();
                bool headerIsPreoptimized = hi->isPreoptimized();
        
                for (i = 0; i < count; i++) {
                  // ↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓
                    Class cls = (Class)classlist[i];
                    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;
                    }
                }
            }
        
        Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
        {
            const char *mangledName = cls->mangledName(); // 读取cls的name
            
            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->superclass = nil;
                return nil;
            }
            
            // Note: Class __ARCLite__'s hack does not go through here. 
            // Class structure fixups that apply to it also need to be 
            // performed in non-lazy realization below.
            
            // These fields should be set to zero because of the 
            // binding of _objc_empty_vtable, but OS X 10.8's dyld 
            // does not bind shared cache absolute symbols as expected.
            // This (and the __ARCLite__ hack below) can be removed 
            // once the simulator drops 10.8 support.
        #if TARGET_OS_SIMULATOR
            if (cls->cache._mask) cls->cache._mask = 0;
            if (cls->cache._occupied) cls->cache._occupied = 0;
            if (cls->ISA()->cache._mask) cls->ISA()->cache._mask = 0;
            if (cls->ISA()->cache._occupied) cls->ISA()->cache._occupied = 0;
        #endif
        
            Class replacing = nil;
            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));
                rw->ro = (class_ro_t *)newCls->data();
                newCls->setData(rw);
                freeIfMutable((char *)old_ro->name);
                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(getClass(mangledName));
            } else {
              	// ↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓
                addNamedClass(cls, mangledName, replacing);
                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;
        }
        
        static void addNamedClass(Class cls, const char *name, Class replacing = nil)
        {
            runtimeLock.assertLocked();
            Class old;
            if ((old = getClass(name))  &&  old != replacing) {
                inform_duplicate(name, old, cls);
        
                // getNonMetaClass 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);
              	// 把类加入gdb_objc_realized_classes这个表里
              	// ↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑
            }
            assert(!(cls->data()->flags & RO_META));
        
            // wrong: constructed classes are already realized when they get here
            // assert(!cls->isRealized());
        }
        
        1. 没有加载:

          realizeClass(cls);

          ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

          // Attach categories

          methodizeClass(cls);

      • 类加载过一次就会有cache信息

    2. 加载protocol --- protocol_map --- protocol_t

    3. 加载方法 ----- nameSelectors ---- SEL

      // Fix up @selector references
          static size_t UnfixedSelectors;
          {
              mutex_locker_t lock(selLock);
              for (EACH_HEADER) {
                  if (hi->isPreoptimized()) continue;
                  
                  bool isBundle = hi->isBundle();
                  SEL *sels = _getObjc2SelectorRefs(hi, &count);
                  UnfixedSelectors += count;
                  for (i = 0; i < count; i++) {
                      const char *name = sel_cname(sels[i]);
                      sels[i] = sel_registerNameNoLock(name, isBundle);
                  }
              }
          }
      

      NXMapInsert(namedSelectors, sel_getName(result), result);

3、load_images

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // 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); // 进入
      	// 1、在里面拿到类的列表,然后递归load所有类和父类
      	/*
      	load过程中的重点:add_class_to_loadable_list
      	
      	↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓
      	loadable_classes[loadable_classes_used].cls = cls;
    		loadable_classes[loadable_classes_used].method = method;
    		****此method为所有类的load方法****
    		↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑
      	*/
      	
      	// 2、在里面拿到所有分类的列表,分类不用递归,因为没有父类
      	/*
      	loadable_categories[loadable_categories_used].cat = cat;
    		loadable_categories[loadable_categories_used].method = method;
      	*/
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();// 调用所有的load方法
}
		do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

// 先类,再分类
// 因为添加类的时候递归添加父类,所以先调用load的时候先调用父类的load方法再调用子类的load方法

4、initialize

  • 先调用父类在调用子类,在main函数之后

    main -> _objc_msgSend_uncached -> _class_lookupMedthodAndLoadCache3 -> lookUpImpForward -> _class_initiallize

    其中有一个父类的递归

  • 如果分类实现initialize,则原类不会调用initialize,先调用父类的分类,再调用子类

    问:是否覆盖?