一、存储结构:
初始化单元存储在一个数组InitContext.InitTable^.UnitInfo中,其中UnitInfo是以数组的方式存储的,其中InitTable的定义为:
其中InitTable定义为packageInfo的类型,再来看看PackageInfo的定义:
可以看到PackageInfo中的UnitInfo被定义为PUnitEntryTable类型,接下来再看PUnitEntryTable的定义:
可以看到单元例程存储在一个数组中,最大可以存储一千万个单元例程,每个例程函数中有Init和FInit两个入口函数,其中Init表示Initialization例程的入口地址,FInit为Finalization例程的入口地址;
结论:delphi初始化例程函数是以一个顺序列表的方式存储的;
二、初始化例程执行顺序:
初始化例程执行函数在System.pas单元的InitUnits函数中:
结论:初始化执行是一个顺序循环执行过程,从0到Count – 1的顺序执行;
Finalization例程的执行是在System.pas单元的FinalizeUnits函数中:
结论:Finalization执行是一个逆序循环执行过程,从Count - 1到0的逆序执行;
三、初始化例程搜索顺序:
单元例程的搜索顺序是编译器完成,无法看到代码,但是可以写一些小的Demo来猜测其搜索顺序;
如果在dpr中引用了Unit1,然后又在Unit1中引用了Unit2,接着在Unit2中引用了Unit3,那么在执行Initialization时先执行Unit3,再是Unit2,最后是Unit1;
如果在dpr中顺序引用Unit1, Unit2, Unit3,但是在Unit1中不引用Unit2,Unit2中不引用Unit3,那么在执行Initialization时先执行Unit1,再是Unit2,最后是Unit3;
结论:首先从dpr文件中加载第一个单元如A,在试图加载A的Initialization时,先查看A的uses部分,这里的uses不分Interface部分还是implementation部分,只分先后顺序,当发现A有Uses单元时,比如依次引用了单元B和C,则先将A压入堆栈,然后处理B,如果B引用了其他单元,处理方式同A,如果没有引用其他单元,则将B的例程保存到UnitEntryTable中(如果UnitEntryTable中已经有了B单元则不保存);接着以相同的方式处理C,当A引用的单元都处理完后将A从堆栈弹出并保存到UnitEntryTable中;其他单元依次处理;