内容简介: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》学习笔记六——方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
XML Hacks
Michael Fitzgerald / O'Reilly Media, Inc. / 2004-07-27 / USD 24.95
Developers and system administrators alike are uncovering the true power of XML, the Extensible Markup Language that enables data to be sent over the Internet from one computer platform to another or ......一起来看看 《XML Hacks》 这本书的介绍吧!