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方法)
相关文章
相关标签/搜索