内容简介:2、3、推论: 当一个类在第一次接受消息时, 会调用他自己的
- 定义
Person类, 继承自NSObject, 并实现+(void)initialize方法
- 定义
Person类的CategoryPerson+Test1, 并实现+(void)initialize方法
- 定义
Person类的CategoryPerson+Test2, 并实现+(void)initialize方法
- 定义
Student类, 继承自Person, 并实现+(void)initialize方法
- 定义
Student类的CategoryStudent+Test1, 并实现+(void)initialize方法
- 定义
Student类的CategoryStudent+Test2, 并实现+(void)initialize方法
二、运行程序
1、不主动调用任何代码, 运行程序
-
main.m中不添加任何代码, 直接运行程序
- 根据结果, 可以知道任何的
+(void)initialize方法没有被调用
2、 Person 类调用 alloc 方法
-
main.m中调用[Person alloc], 底层相当于
objc_msgSend([Person class], @selector(alloc)); 复制代码
- 可以发现,
Person+Test1的代码被调用了 - 查看一下文件的编译顺序, 可以发现
Person+Test1比Person+Test2编译的晚
- 这说明在
Person类通过消息机制调用方法时, 会通过消息机制调用+(void)initialize方法
objc_msgSend([Person class], @selector(initialize)) 复制代码
3、 Student 类调用 alloc 方法
-
main.m中调用[Student alloc], 底层相当于
objc_msgSend([Student class], @selector(alloc)); 复制代码
- 可以看到
[Student alloc]调用时, 会先调用Person类的+(void)initialize方法, 在调用Student类的+(void)initialize方法
推论: 当一个类在第一次接受消息时, 会调用他自己的 +(void)initialize 方法, 如果他有父类, 那么就会优先调用父类的 +(void)initialize 方法
三、查看关于类调用 +(void)initialize 方法的源码
- 在源码里搜索
Method class_getInstanceMethod(Class cls, SEL sel)函数
- 找到代码
lookUpImpOrNil(cls, sel, nil, NO, NO, YES);
- 进入
IMP lookUpImpOrNil(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)函数后, 找到IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
- 进入
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)函数中
- 可以看到有判断, 如果需要初始化, 并且类没有初始化, 那么调用
_class_initialize (_class_getNonMetaClass(cls, inst));
- 进入
void _class_initialize(Class cls)函数中, 我们可以看到, 如果被传入的类有父类, 并且父类没有初始化, 会通过递归将父类传入
- 向下可以找到代码
callInitialize(cls);, 即: 调用初始化方法, 并传入类对象(如果有父类, 父类没有初始化的情况下, 会先初始化父类)
- 进入
void callInitialize(Class cls)函数, 可以看到, 底层是通过消息机制, 调用了类对象的initialize方法
总结:
通过源码可以看到, 当一个类在查找方法的时候, 会先判断当前类是否初始化, 如果没有初始化就会去掉用 initialize 方法
如果这个类的父类没有初始化, 就会先调用父类的 initialize 方法, 再调用自己的 initialize 方法
类在调用 initialize 时, 使用的是 objc_msgSend 消息机制调用
- 所以, 在调用
[Person alloc]方法时, 会使用消息机制调用Person类的initialize方法, 又因为Person存在两个CategoryPerson+Test1和Person+Test2, 此时就看哪一个Category最后一个编译, 就会调用里面的initialize方法 - 在调用
[Student alloc]时, 会调用Student类的initialize方法, 但是因为此时父类Person还没有初始化, 所以会先调用Person的initialize方法
四、移除子类的 +(void)initialize 方法, 再次给子类发送消息
- 删除
Student、Student+Test1和Student+Test2中的initialize方法
- 可以看到,
Person (Test1) - initialize打印了两次, 说明Person的initialize方法被调用了两次 - 前面已经说过, 当
Student类在第一次接收消息时, 会进行初始化, 如果父类没有初始化, 会先给父类初始化 - 所以, 第一次的
Person (Test1) - initialize打印, 实际就是Person类初始化时调用的 - 而一个类只能初始化一次, 所以第二次的打印, 实际是
Student类初始化时调用的 -
Student初始化调用initialize方法时, 用的下面方式
objc_msgSend([Student class], @selector(initialize)) 复制代码
- 在OC中, 使用消息机制调用类方法时, 调用顺序如下:
-
类对象通过isa找到元类对象, 在元类对象的方法列表中查找方法, 如果有就会调用 - 如果
元类对象中没有调用的方法, 就会通过元类对象的superclass找到父类的元类对象, 接着父类的元类对象的方法列表中查找方法, 如果有就会调用
-
- 所以,
Student实际上在初始化时, 调用的是objc_msgSend([Student class], @selector(initialize)), 但是因为Student并没有实现initialize方法, 所以Student调用了父类Person的initialize方法
五、面试题
1、 load 和 initialize 方法的区别是什么?
- 调用方式
-
load是根据函数地址直接调用 -
initialize是通过objc_msgSend调用
-
- 调用时刻
-
load是runtime加载类、分类的时候调用(只会调用一次) -
initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次(如果子类没有实现initialize方法, 会调用父类的initialize方法, 所以父类的initialize方法可能会调用多次)
-
以上所述就是小编给大家介绍的《小码哥iOS学习笔记第六天: initialize方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 统计学习方法-感知机笔记
- 统计学习方法-决策树笔记
- JS常用数组方法总结笔记
- Golang学习笔记 方法和接口
- 《统计学习方法》 ( 李航 ) 读书笔记
- 《Effective Java》学习笔记六——方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Visual C#2005从入门到精通
夏普 / 周靖 / 清华大学出版社 / 2006-6 / 49.00元
《Visual C#2005从入门到精通/微软技术丛书》:微软技术丛书系列之一,建议一读! Microsoft Visual C#功能强大、使用简单。本书全面介绍了如何利用Visual Studio 2005和.NET Framework来进行C#编程。作者将C#的各种特性娓娓道来,以范例导航的方式,通过大量的练习引导读者逐步构建Windows窗体应用程序,访问Microsoft SQL Serv......一起来看看 《Visual C#2005从入门到精通》 这本书的介绍吧!