ObjC load与initialize 简析
+ (void)load
每个类,每个分类中都存在一个
+(void)load
方法。我们不需要显式的进行调用,当runtime动态加载类、分类的时候会进行调用。
创建一个command line 项目 创建几个类。Student继承自Person,每个类包括其Category中都实现load方法,运行我们发现控制台打印了以下信息。
我们发现类中的load方法没有被Category中的load方法覆盖,而是全都进行了调用。这是为什么呢?另外load方法的调用顺序有什么规律呢?
runtime 源码-窥探load方法的调用
通过runtime源码我们可以看到在runtime的入口函数void _objc_init(void)
(存在于objc_os.mm中)中一段代码加载images(镜像)。_dyld_objc_notify_register(&map_images, load_images, unmap_image);
进入load_images可以发现有调用load方法的函数
通过函数名可以知道这里实现对load方法展开调用。查看该方法的实现,我们发现它又是通过call_class_loads对类的load方法进行调用,通过call_category_loads对category中的load方法进行调用的。它使用了一个while循环首先调用类的load方法,类的load方法全部调用完成之后,再调用Category的load方法。
查看call_class_load函数的实现,我们发现它通过遍历一个数组获取一个结构体loadable_class。
这里的method就是load函数的地址,而call_class_loads获取函数地址,直接进行调用。call_category_loads也是如此。
那么loadable_classes这个list是按照什么规律生成的呢?
重新观察load_iamges函数,我们发现有一个prepare_load_methods函数
这个方法遍历classlist,通过schedule_class_load
函数准备class的load方法。这个函数中有递归调用了自己,首先处理父类,最后调用了add_class_to_loadable_list
函数,将load生成loadable_classes
数组和loadable_classes_used
可调用的load方法的数量。
通过这个函数的实现,我们也可以验证上文中所说的loadable_class
中的method就是load方法,我们可以看到method是通过调用getLoadMethod
获取并添加到结构体loadable_class
中的。
Category的将load方法加载到数组的方法跟class基本一致,但是Category是按照编译顺序进行添加的。
+ (void)load的一些问题
综上所述:
+(void)load
方法是在runtime加载类或分类的时候调用的。- 每个类分类的load在程序运行过程中只会调用一次。调用的完成
loadable_classes_used
数量会置为0。 - 先调用类的load方法(先编译的先调用)父类load优先调用
- 分类的load方法 先编译的先调用
- load方法是直接查找到函数地址进行调用的而不是通过消息发送机制调用的,所以不会出现方法覆盖
+(void)initialize
initialize
在一个类刚刚接收到消息的时候进行调用。利用上面的代码,实现initialize
方法,在main函数中让student给alloc发消息。得到以下打印:
经过上面的经验可以得知,initialize是通过objc_msgSend方法调用的,所以只打印了category的initialize中的日志。而category的调用,根据Category的底层实现可以得知,后编译的被调用了。
runtime 源码-窥探initialize方法的调用
由于objc_msgSend的实现没有开源,所以通过查看objc-runtime-new.mm中的获取实例方法的函数(class_getInstanceMethod
)进行窥探。
在该方法中通过调用lookUpImpOrNil
方法查找方法的实现,这个方法又调用了lookUpImpOrForward
方法继续查找。
在lookUpImpOrForward
方法中如果存在initialize
并且没有调用过,则通过_class_initialize (_class_getNonMetaClass(cls, inst));
调用initialize。
_class_initialize (_class_getNonMetaClass(cls, inst));
中通过递归先通过callInitialize(cls);
调用父类的initialize。
callInitialize(cls);
中就可以看出是通过objc_msgSend
调用的。
所以会执行通过isa指针查找方法实现的过程。
+(void)initialize的一些问题
+(void)initialize
是通过objc_msgSend调用的- category实现了initialize方法会覆盖父类的initilize方法
- initialize也只初始化一次,但是由于是通过objc_msgSend的调用的,如果子类没有实现initialize,而父类实现了,则子类每次初始化的时候都会调用父类的
+(void)initialize
,所以父类的+(void)initialize
方法会被调用多次。
ObjC load与initialize 简析相关推荐
- 基于libmad库的MP3解码简析
基于libmad库的MP3解码简析 MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3). ...
- Python源码学习:启动流程简析
Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> Python简介: python主要是动态语言,虽然Python语言也有编译,生成中 ...
- Android Jetpack组件App Startup简析
1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...
- Android 启动过程简析
首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...
- Unity3d资源反编译. AssetBundle格式简析+简单应用+爬坑
=================== Unity3d资源反编译工具 DisUnity ================ 源码:https://github.com/ata4/disunity 需要 ...
- java 进阶笔记线程与并发之ForkJoinPool简析
简介 ForkJoinPool是一个线程池,支持特有的的ForkJoinTask,对于ForkJoinTask任务,通过特定的for与join方法可以优化调度策略,提高效率. 使用 通常,我们继承使用 ...
- Sleep()简析 和Sleep(0)的妙用
Sleep()简析 和Sleep(0)的妙用 原贴地址:残月:Thread.sleep(0)的意义 HawkJony:Sleep(0)的妙用 Thread.Sleep(0) 表示挂起0毫秒,你可能觉得 ...
- 微前端调研及简析SPA实现原理
最近对微前端讨论很多,梳理下自己对微前端的理解以及业内的一些微前端尝试反馈. 第零部分:自己对微前端理解 第一部分:基于Single-SPA微前端的一些demo 第二部分:Single-SPA微前端实 ...
- Learning with Noisy Correspondence for Cross-modal Matching 文献翻译 代码简析
Learning with Noisy Correspondence for Cross-modal Matching 基于噪声对应的跨模态匹配学习 Learning with Noisy Corre ...
最新文章
- 零基础学习大数据开发需要多久能工作?
- html圆圈里面问号,html,css实现问号提示信息
- winpcap编程 解析数据包
- NET问答: 如何实现读写 file 的时候不用锁模式 ?
- rust风化速度_反驳《Golang、Rust的执行速度的对照,让人大吃一惊。》——不会别瞎说...
- 消除数字鸿沟,这些开发者要让代码有“温度”
- 机器学习算法(7)——K近邻(KNN)、K-means、模糊c-均值聚类、DBSCAN与层次与谱聚类算法
- 微信小程序|开发实战篇之六-pagination分页组件
- Nginx Unit 1.8.0 发布,动态 Web 应用服务器
- 我做PM(项目经理)这段时间...
- 第5讲:软考中高项04_进度管理、成本管理
- css属性选择器,[],=, ~=, ^=, ~=, $=, |=等符号含义
- linux安装oracle11g步骤_图解 Debian 10(Buster)安装步骤 | Linux 中国
- 819 c语言程序设计,大连海洋大学2021年考研819高级语言程序设计(C语言)考试大纲...
- 小米10 红米K30Pro 小米10Pro 无限重启卡米 9008救砖后无限重启 线刷无效
- Java 汉字获取拼音或首字母工具类
- 物理专业计算机二级学科,学科门类二级类0702物理学类.doc
- 蓝牙传输速率详细分析【针对蓝牙4.2]
- Android中的UI组件
- @synthesize@dynamic@private,@protected,@publicassign、weak、strong、retain、copy、nonatomic、atomic