iOS 中 load 和 initialize 方法调用机制

+load 方法 

1. 如果一个类实现了load 方法,那么在类被加载到内存的时候就会调用,与这个类是否被用到无关。执行在main函数之前,此时运行环境不安全,不能在这份方法里做过多的操作

当 class 或者 category 添加到 runtime 时调用。即 load 是在这个文件被程序加载时调用,注意:在 load方法中向其它类发送消息,接收消息的类中的+load方法可能尚未被调用。此时不能保证所有的类被加载完成。

2. 程序中所有的类的 load方法都会被系统自动调用,包括各种分类,每个类都只调用一次,并会隐式调用父类的load 方法。

注意:分类和类 load 方法都会调用,他们分别存储在两个全局表中。是分类 load 不覆盖原来类的 load 方法的本质;

两个 while 循环,先通过 call_class_loads 执行所有类的 load 方法,再通过 call_category_loads 执行分类的 load 方法。

3.  调用类的 load 方法前,先调用父类的 load方法(递归),父类方法优先于子类调用,分类load 方法 最后调用。

schedule_class_load(cls->superclass);  递归调用父类load方法

注⚠️: load 直接用函数地址调用,不是objc_msgSend 发送消息,不存在方法查找和消息传递机制;也就是说如果类实现了 load 函数就会调用,当类未实现load方法时,不会调用父类load方法。

4. 先编译的分类, 优先调用load,当有多个类别(Category) 都实现了load  方法,这几个load方法都会执行, 执行顺序和分类 在 Compile Sources中出现的顺序一致。

注意:父类Catregory 与子类 Category 的 load 方法执行顺序:由分类在编译器中的编译顺序决定,与其在Compile Sources 出现顺序一致。

5. 先编译的类, 优先调用load,,有多个不同的类的时候, 每个类 load 执行顺序与 在Compile Sources 出现的顺序一致。 

6. load方法方法内部使用了锁,是线程安全的。有一定的性能开销会很微弱的影响启动时间,重写方法时要尽可能保持简单,避免阻塞线程,不要再使用锁。

7. 在 load方法中向其它类发送消息,接收消息的类中的+load方法可能尚未被调用。

注意:load调用时机比较早,当load调用时,其他类可能还没加载完成,运行环境不安全.

8. 调用优先级:父类>子类>分类,并且不会被覆盖,均会调用

日常使用场景 :是线程安全的,一定会调用且只调用一次

1. 通常在使用UrlRouter的时候注册类的时候也在+load方法中注册

2. 用来交换方法Method Swizzle

应用:因为类或者分类一旦被加载到运行时,就会调用这个方法;因为加载时间特别早:所以可以利用这个特性进行一些处理

问题:在子类的load  [super load]  会调用到哪个类中?

 [super load] 将调用到 类最后一个被编译的分类的 load 方法,因为这里是消息发送,而不是通过方法指针。

添加处理category到类的工作会先于类的加载处理 (+load方法的执行)。

在load方法中可以调用category中声明的方法。


initialize 方法

1. 是懒加载,在类或者其子类第一次使用时调用。调用顺序在 main 方法之后,实例化对象之前。

注意:即使类文件被引用进项目,如果一直没有使用这个类,方法不会被执行。

2. 理论上类的 initialize 方法 只会调用一次

注意: initialize 方法是通过 objc_msgSend 调用的,经过方法查找和消息转发的过程。如果子类没有实现 initialize,就会调用父类的 initialize 方法,因此父类  initialize 方法 存在多次调用的可能。

例子:子类不实现initialize方法,会调用父类的。父类初始化时, 已经调用过一次自己initialize方法.  父类initialize 方法可能执行2次 

使用场景:在initialize 方法中使用hook,做方法替换就可能会出现多次替换,导致方法替换失效。可以使用类型判断避免这种问题。

保证该类只被调用一次

3.  父类调用一定在子类之前。初始化子类时,系统会默认初始化父类先。

🐳🐳🐳:如果先引用父类的实例对象,再引用子类实例对象,则会在引用父类实例对象时调用父类 initialize 方法;用子类实例对象时,由于父类的 initialize 方法已经执行,所以此时只调用子类 initialize 方法。

递归初始化

⚠️⚠️:初始化自己之前,递归执行父类的初始化操作;先判断父类有没有初始化initialize 完成,如果没有则递归执行父类的初始化,父类的initialize 调用 > 子类initialize 调用。

4. 分类的 initialize 方法会覆盖原类的 initialize。(Person、或 Person+Category)都是一个Perosn类,只会执行分类的initialize, 原类的initialize 不再执行。先初始化分类, 后初始化子类。

注意: 如有多个分类,只执行最后被编译的分类的 initialize 方法,其他分类的 initialize 方法都会失效;生效的是在 Compile Sources列表中最后一个 Category。

5. initialize 方法内部使用了锁,是线程安全的,可能会阻塞线程,方法中应做一些简单不复杂的类初始化的前期准备工作。

注意:initialize方法中运行环境基本健全。,initialize方法一般用于初始化全局变量或静态变量。

6. 调用优先级:分类>父类,父类>子类。

如果分类和父类均实现了+initialize,则只有分类的+initialize会被调用;

如果父类和子类均实现了+initialize,第一次引用 子类时,先调用父类的+initialize,再调用子类的+initialize;

如果子类未实现initialize 方法,父类实现了+initialize,则第一次引用子类时,会调用两次父类的+initialize


其他:

realizeClass:realizeClass 处理后的类才是『真正的』类,调用时不能对类做写操作。

类初始化之前,objc_class->data() 返回的指针指向 class_ro_t 结构体。等 static Class realizeClass(Class cls) 静态方法在类第一次初始化时被调用,它会开辟 class_rw_t 的空间,并将 class_ro_t 指针赋值给 class_rw_t->ro。


懒加载类优点:1. 加快启动速度   2. 节省应用初始内存

1. 如果一个类实现了 + load 方法,这个类是 non-lazy class(非懒加载类)。如果所有的类都在启动的时候调用load方法,就会非常慢。

2. 没实现 + load 方法,是懒加载类。等到调用的时候再去实现,可以加快启动速度。

每个类都有很多的代码,包括变量,方法等,会占用很多内存,如果这个类在工程中就没调用,或者在很深的页面才会调用,正常情况下很少会使用到,如果在启动时加载了,就会造成内存浪费。

getter  也可使用懒加载方式


关系图


在dyld调用静态构造函数之前,libc 会调用 _objc_init()

objc_init会调用map_images 方法中-- > 会调用 read_images方法

read_images  中会处理分类,把分类加到类上(方法,属性等),然后进行类的加载处理调用 load 方法等操作。


参考:

iOS +load()详解 - 简书

iOS类方法load和initialize详解 - 简书

load和initialize方法的区别是什么? - 简书

load 与 initialize 方法 - 简书

彻底搞懂+load和+initialize_康小曹(简书)-CSDN博客

iOS-底层原理14:dyld与objc的关联 - 简书

iOS 懒加载类和非懒加载类 - 简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,523评论 0 1
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,133评论 0 12
  • 最近我常常在想,我到底想要什么呢? 年少时,有好多好多梦想,对生活有太多期许,等到大学毕业踏入社会,才发现很多事情...
    陌上心言阅读 1,400评论 0 0
  • 前几天偶遇一个香港商界精英,听他讲了一段曾经经商的经历,深感香港经商的不易。 几年前,他和几个朋友合伙开了面包屋,...
    潘燕生阅读 884评论 2 6