iOS底層探索:從dyld到main 一、dyld內部的主要流程 二、map_images流程 三、load_images流程

問自己兩個問題:
1.應用啓動在main函數之前到底做了什麼事情?
2.類、分類中load方法的加載順序怎樣的?分類中出現的與主類同名的方法,會調用哪一個呢?

這些問題,不跟蹤一次底層的源碼?怎麼會領悟得透徹呢?
我們實現一個類,然後在load方法中打個斷點看看:

這個調用棧的信息就非常豐富,從dyld-_dyld_start開始,經歷了一系列步驟,最終進入了load_images,在load_images方法中調用了load方法。

我們到Apple Open Source網站下載一份最新的dyld源碼、system 源碼、 dispatch源碼、objc源碼。(目前最新版本分別是dyld-852.2,Libsystem-1292.120.1,libdispatch-1271.120.2,objc4-824),然後就可以根據load斷點打印的調用棧來一點點跟。

一、dyld內部的主要流程

__dyld_start開始於dyldStartup.s彙編代碼中:


這裏標註了__dyld_start調用了dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)開始初始化,最後調用了main函數進入主程序。
所以重點就在dyldbootstrap::start流程中了,由於步驟較多,我們就通過流程圖來表示,具體如下:

簡單來說:main之前通過,dyld對主程序運行環境進行初始化,生成ImageLoader把動態庫生成的image加載到內存中,然後進行鏈接綁定,接着初始化所有動態庫,再接着初始化libSystemlibdispatchlibobjc。其中libobjc的初始化函數_objc_init,初始化運行環境,並且通過_dyld_objc_notify_register(&map_images, load_images, unmap_image)註冊讀取images,加載images的函數。

二、map_images流程

2.1map_images主要用以處理dyld給出的image,該函數直接進入了map_images_nolock函數。接下來我們先看看這個函數主要作惡什麼事情:

void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) {

   if (firstTime) { 
        /*首次執行作一些必要的初始化*/
        preopt_init();
   }

   /*通過image的地址,創建header_info信息,並且添加到hList中*/
   /*mach_header對應每個動態庫的macho*/

   auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
   hList[hCount++] = hi;

   if (firstTime) {
        /*namedSelectors初始化*/
        sel_init(selrefCount);
        
        /*AutoreleasePoolPage::init();//自動釋放池初始化
          SideTablesMap.init();//散列表初始化
          _objc_associations_init();//關聯表初始化
       */
        arr_init();
    }
     
    if (hCount > 0) {
          /*準備工作完成後就會進入_read_images*/
         _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
}

2.2 接下來在看_read_images的流程

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
      /*先看一下這個宏定義,他是for循環的條件語句,每次取出hi = hList[hIndex],並且hIndex++。所以在下面會看到EACH_HEADER的寫法*/
    #define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++
  
  /*1.初始化TaggedPointer, 創建gdb_objc_realized_classes*/
  if (!doneOnce) {
     doneOnce =  true;

    if (DisableTaggedPointers) {
        disableTaggedPointers();
    }     
    initializeTaggedPointerObfuscator();

    int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
    gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
  }


  /*2.Fix up @selector references*/
  static size_t UnfixedSelectors;
  {
     for (EACH_HEADER) {
         SEL *sels = _getObjc2SelectorRefs(hi, &count);
         for (i = 0; i < count; i++) {
            const char *name = sel_cname(sels[i]);
            /*namedSelectors.get().insert(name)*/
            SEL sel = sel_registerNameNoLock(name, isBundle);
            if (sels[i] != sel) {
                 sels[i] = sel;
            }
        }
    }
 }
   
  /*3.Discover classes. Fix up unresolved future classes. Mark bundle classes*/
  for(EACH_HEADER){
      classref_t const *classlist = _getObjc2ClassList(hi, &count);
      for (i = 0; i < count; i++) {
         Class cls = (Class)classlist[I];
         
          /*readClass:舊類改動後會生成新的類,並重映射到新的類上*/
          /*mangledName !=nil: NXMapInsert(nonMetaClasses(), cls->ISA(), cls) || NXMapInsert(gdb_objc_realized_classes, name, cls)*/
          /*mangledName ==ni:類自身和元類allocatedClasses.insert(cls)*/
         Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
         if (newCls != cls  &&  newCls) {
              resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
              resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
         }
       }
    }

    /*4.Fix up remapped classes*/
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
            // fixme why doesn't test future1 catch the absence of this?
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
        }
    }


    /*5.Fix up old objc_msgSend_fixup call sites*/
    for (EACH_HEADER) {
        message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
        for (i = 0; i < count; i++) {
            fixupMessageRef(refs+i);
        }
    }

  /*6.Discover protocols. Fix up protocol refs*/
   for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        ASSERT(cls);
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->hasPreoptimizedProtocols();
        bool isBundle = hi->isBundle();
        protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map,  isPreoptimized, isBundle);
        }
    }
    //Fix up @protocol references
    for (EACH_HEADER) {
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }

    /* 7.Discover categories. Only do this after the initial category attachment has been done*/
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            /*cls->isStubClass():objc::unattachedCategories.addForClass(lc, cls);*/
            /*First, register the category with its target class. Then, rebuild the class's method lists (etc) if the class is realized*/
            load_categories_nolock(hi);
        }
    }

     /*8.Realize non-lazy classes (for +load methods and static instances)*/
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            addClassTableEntry(cls);
            realizeClassWithoutSwift(cls, nil);
        }
    }

    /*9.Realize newly-resolved future classes, in case CF manipulates them*/
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            realizeClassWithoutSwift(cls, nil);
            cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }
}
static void load_categories_nolock(header_info *hi) {
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    size_t count;
    auto processCatlist = [&](category_t * const *catlist) {
        for (unsigned i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{cat, hi};
            if (!cls) {
                // Category's target class is missing (probably weak-linked). Ignore the category.
                continue;
            }

            // Process this category.
            if (cls->isStubClass()) {
                /* Stub classes are never realized. Stub classes don't know their metaclass until they're  initialized, so we have to add categories with  class methods or properties to the stub itself. methodizeClass() will find them and add them to  the metaclass as appropriate.*/
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                /* First, register the category with its target class. Then, rebuild the class's method lists (etc) if  the class is realized.*/
                if (cat->instanceMethods ||  cat->protocols ||  cat->instanceProperties) {
                 /*實例方法/實例屬性:class本身已經實現了,會將相關方法屬性添加到class,否則添加到unattachedCategories*/
                    if (cls->isRealized()) {
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    }else {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }

                if (cat->classMethods  ||  cat->protocols  ||  (hasClassProperties && cat->_classProperties)){
                     /*類方法/類屬性:class-isa本身已經實現了,會將相關方法屬性添加到metadata-class,否則添加到unattachedCategories*/
                    if (cls->ISA()->isRealized()) {
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };

    processCatlist(hi->catlist(&count));
    processCatlist(hi->catlist2(&count));
}

三、load_images流程

入口函數,準備load方法,調用load方法
void load_images(const char *path __unused, const struct mach_header *mh){
    prepare_load_methods((const headerType *)mh);// Discover +load methods
    call_load_methods(); // Call +load methods (without classLock - re-entrant)
}

3.1準備所有的load方法,把包含load方法的class,category添加到對應的表中

void prepare_load_methods(const headerType *mhdr){
    size_t count, i;

    runtimeLock.assertLocked();

    /*superclsss->class:添加到loadable_classes表中,實體類型loadable_class*/
    classref_t const *classlist =  _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    /*category:添加到loadable_categories表中,實體類型loadable_category*/
    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        add_category_to_loadable_list(categorylist[i]);
    }
}

class添加到loadable_classes表中,加入的是loadable_class類型(包含cls和load-method, getLoadMethod就是查找load方法);分類添加到loadable_categories表中,加入的是loadable_category類型(包含category和load-method)

static void schedule_class_load(Class cls){
   ...
    /*先插入superclass, 再插入class!!load方法的調用順序*/
    schedule_class_load(cls->getSuperclass());
    add_class_to_loadable_list(cls);
   ...
}

/*沒有load方法的類不會加進來,會在已經申請的內存用完的情況繼續擴容,loadable_classes_used作爲計數器*/
void add_class_to_loadable_list(Class cls){
    IMP method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *) realloc(loadable_classes,  loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

/*沒有load方法的類不會加進來,會在已經申請的內存用完的情況繼續擴容,loadable_categories_used作爲計數器*/
void add_category_to_loadable_list(Category cat){
    IMP method = _category_getLoadMethod(cat);
    if (!method) return;  // Don't bother if cat has no +load method
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *) realloc(loadable_categories, loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}

/* loadable_class結構體*/
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

/* loadable_category結構體*/
struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};

3.2調用load方法

void call_load_methods(void){
    ...
    do {
       
        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);
    ...
}

//調用class-load方法,loadable_classes_used=0
static void call_class_loads(void){
   ...
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 
        (*load_method)(cls, @selector(load));
    }
    ...
}

//調用category-load方法,loadable_categories_used=0
static bool call_category_loads(void){
    ...
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;
      
        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            (*load_method)(cls, @selector(load));
            cats[i].cat = nil;
        }
    }
    ...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章