iOS 底层第13天的学习。在第12天的学习中,已经梳理了 dyld 整个的流程。但是在registerObjCNotifiers调用三个参数时候,是何时进行调用和执行的还不清楚,接下来具体分析。
registerObjCNotifiers
- 当
libobjc:_objc_init调用_dyld_objc_notify_register会传入 3 个参数
void _objc_init(void)
{
//... 省略 一些 init 的方法
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
dyld:: registerObjCNotifiers
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
}
| libojc | dyld |
|---|---|
&map_images |
sNotifyObjCMapped |
load_images |
sNotifyObjCInit |
unmap_image |
sNotifyObjCUnmapped |
- 我们得知在
registerObjCNotifiers对3个参数进行了赋值,而何时调用还不清楚
接下来我们要对这几个参数如何调用来进行分析
sNotifyObjCMapped
- 全局搜索
sNotifyObjCMapped
static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
// ... 省略部分代码
if ( objcImageCount != 0 ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
uint64_t t0 = mach_absolute_time();
// 在这里进行了调用
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
uint64_t t1 = mach_absolute_time();
ImageLoader::fgTotalObjCSetupTime += (t1-t0);
}
// ....
- 在
notifyBatchPartial进行了sNotifyObjCMapped调用,全局搜索sNotifyObjCMapped
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
}
- 发现在
registerObjCNotifiers里就直接调用了notifyBatchPartial
sNotifyObjCInit
- 全局搜索
sNotifyObjCInit
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
// ... 省略部分代码
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
- 发现在
notifySingle进行了调用,全局搜索notifySingle,看看那在何时调用了notifySingle
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
// let objc know we are about to initialize this image
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
// 在这里调用了 context.notifySingle
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_initialized, this, NULL);
}
- 发现在
recursiveInitialization进行了notifySingle的调用
但这时有个疑问就是
notifySingle为何在doInitialization(初始化所有images的赋值)之前进行调用呢,不是应该在赋值完 之后再进行notifySingle调用嘛?
- 因为
recursiveInitialization这是一个递归调用,当第一次进来的时候是libsystem没有依赖其他底层文件 ,直接会进入doInitialization进行初始化,初始化完毕后再调用context.notifySingle
sNotifyObjCUnmapped
- 全局搜索
sNotifyObjCUnmapped
void removeImage(ImageLoader* image) {
// ... 省略部分代码
if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() )
(*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());
}
- 在
removeImage时调用了sNotifyObjCUnmapped,继续搜索removeImage
// This function is called at the end of dlclose() when the reference count goes to zero.
// The dylib being unloaded may have brought in other dependent dylibs when it was loaded.
// Those dependent dylibs need to be unloaded, but only if they are not referenced by
// something else. We use a standard mark and sweep garbage collection.
//
void garbageCollectImages()
{
do {
sRedo = false;
// ... 省略部分代码
// collect phase: delete all images which are not marked in-use
bool mightBeMore;
do {
mightBeMore = false;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( ! image->isMarkedInUse() ) {
try {
if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName());
removeImage(image);
ImageLoader::deleteImage(image);
mightBeMore = true;
break; // interator in invalidated by this removal
}
catch (const char* msg) {
dyld::warn("problem deleting image: %s\n", msg);
}
}
}
} while ( mightBeMore );
} while (sRedo);
}
- 发现
garbageCollectImages调用了removeImage,根据注解当dyld没有加载到dependent dylibs就进行此方法的调用
C++ 函数在何时进行调用
- 新建一个工程,代码👇
main
int main(int argc, char * argv[]) {
NSLog(@">>>> 调用了 : %s \n",__func__);
}
c++
__attribute__((constructor)) void cxxFunc(){
printf(">>>> 调用了 : %s \n",__func__);
}
load
+ (void) load {
NSLog(@">>>> 调用了 : %s \n",__func__);
}
- 运行后如下

- 可知在
load后加载了cxxFunc,那在何时调用的呢? - 下个断点

-
bt打印堆栈
- 发现在
ImageLoaderMachO::doModInitFunctions调用了c++方法
流程总结

