load VS initialize

load相關問題

Category中有load方法嗎?

有load方法。web

調用時機

load方法在runtime加載類、分類的時候調用,調用方式是直接調用方法,而不是經過消息機制觸發調用。編輯器

load 方法能繼承嗎?

load方法能夠繼承,可是通常狀況下不會主動去調用load方法,都是讓系統自動調用。函數

調用順序

  1. 先調用類的load方法。ui

    按照編譯前後順序調用,先編譯的先調用。url

  2. 調用子類的load方法以前會先調用父類的load方法。spa

  3. 再調用分類的load方法。ssr

    按照編譯前後順序調用,先編譯的先調用。code

源碼

// objc-os.mm/_objc_init(void)
void _objc_init(void) {  _dyld_objc_notify_register(&map_images, load_images, unmap_image); }  // objc-runtime-new.mm/load_images void load_images(const char *path __unused, const struct mach_header *mh) {  prepare_load_methods((const headerType *)mh);  call_load_methods(); }  // objc-runtime-new.mm / prepare_load_methods  void prepare_load_methods(const headerType *mhdr) {  size_t count, i;  runtimeLock.assertWriting();   //獲取全部的類  classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);  for (i = 0; i < count; i++) {  // 整理要調用load方法的類  schedule_class_load(remapClass(classlist[i]));  }  //獲取全部的分類  category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);  for (i = 0; i < count; i++) {  category_t *cat = categorylist[i];  Class cls = remapClass(cat->cls);  if (!cls) continue; // category for ignored weak-linked class  realizeClass(cls);  assert(cls->ISA()->isRealized());  add_category_to_loadable_list(cat);  } }  // objc-runtime-new.mm / schedule_class_load  static void schedule_class_load(Class cls) {  if (!cls) return;  assert(cls->isRealized()); // _read_images should realize   //類是否已經被加載到loadable_classes  if (cls->data()->flags & RW_LOADED) return;   // 確保父類排在前面  schedule_class_load(cls->superclass);   add_class_to_loadable_list(cls);  // 標誌該類已經添加到了loadable_classes  cls->setInfo(RW_LOADED); }  // objc-loadmethod.mm / add_class_to_loadable_list void add_class_to_loadable_list(Class cls) {  IMP method;   loadMethodLock.assertLocked();   method = cls->getLoadMethod();  if (!method) return; // Don't bother if cls has no +load method   if (PrintLoading) {  _objc_inform("LOAD: class '%s' scheduled for +load",  cls->nameForLogging());  }   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++; }  //objc-loadmethod.mm / call_load_methods 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) {  //調用類的load方法  call_class_loads();  }   // 2. Call category +loads ONCE  // 調用Category的load方法  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; }  //objc-loadmethod.mm / call_class_loads 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方法  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方法。  (*load_method)(cls, SEL_load);  }   // Destroy the detached list.  if (classes) free(classes); }  複製代碼

initialize 相關問題

調用時機

initialize會在類第一次接受到消息的時候調用。與load方法不一樣的是initialize是經過消息機制調用的即經過objc_msgsend()調用。orm

調用順序

根據調用順序觸發。繼承

調用子類的initialize會先觸發父類的initialize

// objc-runtime-new.mm / class_getInstanceMethod
Method class_getInstanceMethod(Class cls, SEL sel) {  if (!cls || !sel) return nil;  lookUpImpOrNil(cls, sel, nil,  NO/*initialize*/, NO/*cache*/, YES/*resolver*/);  return _class_getMethod(cls, sel); }  // objc-runtime-new.mm / lookUpImpOrNil IMP lookUpImpOrNil(Class cls, SEL sel, id inst,  bool initialize, bool cache, bool resolver) {  IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);  if (imp == _objc_msgForward_impcache) return nil;  else return imp; }  // objc-runtime-new.mm / lookUpImpOrForward IMP lookUpImpOrForward(Class cls, SEL sel, id inst,  bool initialize, bool cache, bool resolver) {  IMP imp = nil;   runtimeLock.read();   if (initialize && !cls->isInitialized()) {  runtimeLock.unlockRead();  _class_initialize (_class_getNonMetaClass(cls, inst));  runtimeLock.read();  }   return imp; }  // objc-initialize.mm / _class_initialize void _class_initialize(Class cls) {  assert(!cls->isMetaClass());  supercls = cls->superclass;  // 遞歸調用,先調用父類的 initialize  if (supercls && !supercls->isInitialized()) {  _class_initialize(supercls);  }  callInitialize(cls); }  // objc-initialize.mm / callInitialize void callInitialize(Class cls) {  // 經過 objc_msgSend 進行調用  ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);  asm(""); } 複製代碼

總結

load、initialize方法的區別什麼?

調用方式

  1. load是根據函數地址直接調用
  2. initialize是經過objc_msgSend調用

調用時刻

  1. load是runtime加載類、分類的時候調用(只會調用1次)
  2. initialize是類第一次接收到消息的時候調用,每個類只會initialize一次(父類的initialize方法可能會被調用屢次)

load、initialize的調用順序?

  1. load

    1. 先調用類的load
      1. 先編譯的類,優先調用load
      2. 調用子類的load以前,會先調用父類的load
  2. 再調用分類的load

    1. 先編譯的分類,優先調用load
  3. initialize

    1. 先初始化父類
    2. 再初始化子類(可能最終調用的是父類的initialize方法)
相關文章
相關標籤/搜索