runtime載入過程

jialebaba發表於2019-02-25

iOS程式載入過程

  1. 在程式啟動之後,由dyld(動態連結編輯器)將程式載入到二進位制中,完成一些檔案的初始化操作。
  2. Runtime向dyld中註冊回撥函式。
  3. 通過ImageLoader將所有的image載入到記憶體中。
  4. dyld在images發生改變時主動呼叫回撥函式。
  5. Runtime在接收到回撥函式後開始執行map_images,load_images等函式,並回撥+load函式。
  6. 呼叫main()函式,開始執行業務程式碼。

map_images

在Runtime載入過程中,會呼叫_objc_init函式,在_objc_init函式中主要呼叫了map_images,load_images等方法

void _objc_init(void) {
// .... 各種init
     _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
//主要作用是一個呼叫中轉
 void map_images(unsigned count, const char * const paths[], const struct mach_header * const mhdrs[])
{
    rwlock_writer_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
    if (hCount > 0) {
         _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
      }
}
複製程式碼

read_images主要邏輯:

  1. 載入所有類到gbd_objc_readlized_classes表中
  2. 對所有類做重對映
  3. 將所有SEL都註冊到namedSelectors表中
  4. 修復函式指標遺留
  5. 將所有的Protocol都新增到protocol_map表中
  6. 多所有的Protocol做重對映
  7. 初始化所有非懶載入的類,進行rw,ro操作
  8. 遍歷已標記的懶載入的類,並做初始化操作
  9. 處理所有Category,包括Class和Meta Class
  10. 初始化所有未初始化的類
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unopt imizedTotalClasses)
{
    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;
    Class *resolvedFutureClasses = nil;
    size_t resolvedFutureClassCount = 0; 
    static bool doneOnce;
    TimeLogger ts(PrintImageTimes);
#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++
    if (!doneOnce) { 
         doneOnce = YES;
         //例項化儲存類的雜湊表,並根據當前類數量做動態擴容
         int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
         gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
     }
//由編輯器讀取類列表,並將所有的類新增到雜湊表中,並標記懶載入的類並初始化空間
    for (EACH_HEADER) {
         if (! mustReadClasses(hi)) { 
            continue;
         }
         bool headerIsBundle = hi->isBundle();
         bool headerIsPreoptimized = hi->isPreoptimized();
         /** 將新類新增到雜湊表中 */
         // 從編譯後的類列表中取出所有類,獲取到一個classref_t型別指標
         classref_t *classlist = _getObjc2ClassList(hi, &count);
         for (i = 0; i < count; i++) {
             // 陣列中取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunLoop等系統類<CF、Fundation、liddispatch等>和自己建立的類
             Class cls = (Class)classlist[i];
             // 通過readClass函式獲取處理過後的新類,內部主要操作ro和rw結構體
             Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
             // 初始化所有懶載入的類需要的記憶體空間
             if (newCls != cls && newCls) {
                 // 將懶載入的類新增到陣列中 
                 resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
                 resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
             }
         }
     }

//將為對映的Class和superClass重對映,被remap的類都是非懶載入的類
     if (!noClassesRemapped()) {
         for (EACH_HEADER) {
         // 重對映Class,從_getObjc2ClassRefs函式中取出類的引用
             Class *classrefs = _getObjc2ClassRefs(hi, &count);
             for (i = 0; i < count; i++) {
                 remapClassRef(&classrefs[i]);
             }
         } 
         // 重對映父類
         classrefs = _getObjc2SuperRefs(hi, &count); 
         for (i = 0; i < count; i++) {
             remapClassRef(&classrefs[i]);
         }
     }

     // 對SEL做重對映
     static size_t UnfixedSelectors; sel_lock();
     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]);
             //註冊SEL
             sels[i] = sel_registerNameNoLock(name, isBundle);
         } 
     }
     // 修復舊的函式指標遺留
     for (EACH_HEADER) {
         message_ref_t *refs = _getObjc2MessageRefs(hi, &count); 
         if (count == 0) continue;
         for (i = 0; i < count; i++) {
             // 在內部將常用的alloc、objc_msgSend等函式指標進行註冊,並fix為新的函式指標
             fixupMessageRef(refs+i);
         }
     }
     // 遍歷所有協議列表,並且將協議列表載入到Protocol的雜湊表中
     for (EACH_HEADER) {
         extern objc_class OBJC_CLASS_$_Protocol;
         // cls = Protocol類,所有協議和物件的結構體都類似,isa對應Protocol類
         Class cls = (Class)&OBJC_CLASS_$_Protocol;
         assert(cls);
         // 獲取protocol雜湊表
         NXMapTable *protocol_map = protocols();
         bool isPreoptimized = hi->isPreoptimized();
         bool isBundle = hi->isBundle();
         // 從編譯器中讀取並初始化Protocol
         protocol_t **protolist = _getObjc2ProtocolList(hi, &count); 
         for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map,
                         isPreoptimized, isBundle);
         }
     }
     // 修復協議列表應用,優化後的images不確定正確與否
     for (EACH_HEADER) {
         //下面的_getObjc2ProtocolRefs函式與上面的_getObjc2ProtocolRefs不同
         protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
         for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
         }
     }
     
     // 實現非懶載入的類
     for (EACH_HEADER) {
         classref_t *classlist =
            _getObjc2NonlazyClassList(hi, &count);
         for (i = 0; i < count; i++) {
             Class cls = remapClass(classlist[i]);
             if (!cls) continue;
             //實現非來載入的類(例項化一些資訊,如rw)
             realizeClass(cls);
         }    
     }
     
     // 遍歷resolvedFutureClasses陣列,實現所有懶載入的類
     if (resolvedFutureClasses) {
         for (i = 0; i < resolvedFutureClassCount; i++) {
             // 實現懶載入的類
             realizeClass(resolvedFutureClasses[i]); resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
         }
        free(resolvedFutureClasses);
     }
     
// 發現和處理所有Category
     for (EACH_HEADER) {
         // 外部迴圈遍歷找到當前類,查詢類對應的Category陣列
         category_t **catlist = _getObjc2CategoryList(hi, &count);
         bool hasClassProperties = hi->info()->hasCategoryClassProperties();

         內部迴圈遍歷當前類的所有的Category 
         for (i = 0; i < count; i++) {
             category_t *cat = catlist[i];
             Class cls = remapClass(cat->cls);
             // 首先,通過所屬的類註冊Category。如果這個類已經被實現,則重新構造類的方法列表 
             bool classExists = NO;
             if (cat->instanceMethods || cat->protocols || cat->instanceProperties) {
                 // 將Category新增到對應的value中,value是Class對應的所有的Category陣列
                 addUnattachedCategoryForClass(cat, cls, hi);
                 // 將Category的method,protocol,property新增到Class中
                 if (cls->isRealized()) {
                     remethodizeClass(cls);
                     classExists = YES;
                 }
             }
             // 與下面邏輯相同,不過是在Meta Class中進行操作
             if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) {
                 addUnattachedCategoryForClass(cat, cls->ISA(), hi); 
                 if (cls->ISA()->isRealized()) {
                     remethodizeClass(cls->ISA());
                 }

             }
         }
     }
     
     // 初始化所有的類,Category必須是最後執行
     // DebugNonFragileIvars = -1,不執行
     if (DebugNonFragileIvars) {
         realizeAllClasses();
     }
#undef EACH_HEADER
}
複製程式碼

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);
                 //主要查詢Class、Category的方法方法
                 prepare_load_methods((const headerType *)mh);
             }
             // Call +load methods (without runtimeLock - re-entrant)
             // 呼叫Class、Category的方法列表
              call_load_methods();
     }
     
     
     void prepare_load_methods(const headerType *mhdr) {
         size_t count, i;
         runtimeLock.assertLocked();
         // 獲取到非懶載入類的列表
         classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
         for (i = 0; i < count; i++) {
             // 設定Class的呼叫列表
             schedule_class_load(remapClass(classlist[i]));
         }
         // 獲取Category的列表
         category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
         for (i = 0; i < count; i++) {
             category_t *cat = categorylist[i];
             Class cls = remapClass(cat->cls);
             // category for ignored weak-linked class,忽略弱連結<不懂>的類別
             if (!cls) continue;  
             // 例項化所屬的類
             realizeClass(cls);
             assert(cls->ISA()->isRealized());
             // 設定Categoty的呼叫列表
             add_category_to_loadable_list(cat);
         }
     }
     static void schedule_class_load(Class cls) {
         
         if (!cls) return;
         // _read_images should realize,_read_images是否完成
         assert(cls->isRealized());  
         // 已經新增完成,則return
         if (cls->data()->flags & RW_LOADED) return;

         // Ensure superclass-first ordering,確保superClass新增
         schedule_class_load(cls->superclass);
         // 新增imp、class到呼叫中
         add_class_to_loadable_list(cls);  
         // 設定class的識別符號,表示已新增到list中
         cls->setInfo(RW_LOADED); 
     }
    
     // 在obj-loadmethod.mm中
     void add_category_to_loadable_list(Category cat) {
         IMP method;
         // 獲取Category中Load方法的imp
         loadMethodLock.assertLocked();
         method = _category_getLoadMethod(cat);
         // Don't bother if cat has no +load method,沒有則return
         if (!method) return;

         if (PrintLoading) {
             _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
         }
             如果已使用大小等於陣列大小,對陣列進行動態擴容
         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_calss、loadable_categories陣列,load_images會通過call_load_methods函式執行這些load方法
     
     void call_load_methods(void) {
         static bool loading = NO;
         bool more_categories;

         loadMethodLock.assertLocked();

         // Re-entrant calls do nothing; the outermost call will finish the job.防止重新進入
         if (loading) return;
         loading = YES;

         void *pool = objc_autoreleasePoolPush();

         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);
         objc_autoreleasePoolPop(pool);
         loading = NO;
     }
     
     // 通過遍歷獲得loadable_class結構體
     static void call_class_loads(void) {
         int i;
    
         // Detach current loadable list.
         struct loadable_class *classes = loadable_classes;
         int used = loadable_classes_used;
         loadable_classes = nil;
         loadable_classes_allocated = 0;
         loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
         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; 

             if (PrintLoading) {
                 _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
             }
             (*load_method)(cls, SEL_load);
         }
    // Destroy the detached list.
         if (classes) free(classes);
     }
     
     
     struct loadable_class {
         Class cls; // may be nil 
         IMP method;
     };
    
複製程式碼
  1. 通過上述程式碼,發現load方法的呼叫順序為 父類 -> 子類 -> Category。
  2. category的 load方法會覆蓋 class的 load方法

相關文章