程序启动后会调用_objc_init,然后会进行一些初始的操作,如下源码显示:
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init(); -- tls是线程的局部存储
static_init(); -- 运行c++的一些静态函数
runtime_init();
exception_init();
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
首先在我们Xcode里设置环境变量也就是打开load方法的输出,我们设置为OBJC_PRINT_LOAD_METHODS,然后看下运行代码后打印的。

objc[29649]: LOAD: class 'NSNotificationCenter' scheduled for +load
objc[29649]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[29649]: LOAD: +[NSNotificationCenter load]
objc[29649]: LOAD: +[NSObject(NSObject) load]
objc[29649]: LOAD: class 'NSColor' scheduled for +load
objc[29649]: LOAD: class 'NSApplication' scheduled for +load
objc[29649]: LOAD: class 'NSBinder' scheduled for +load
objc[29649]: LOAD: class 'NSColorSpaceColor' scheduled for +load
objc[29649]: LOAD: class 'NSNextStepFrame' scheduled for +load
objc[29649]: LOAD: +[NSColor load]
objc[29649]: LOAD: +[NSApplication load]
objc[29649]: LOAD: +[NSBinder load]
objc[29649]: LOAD: +[NSColorSpaceColor load]
objc[29649]: LOAD: +[NSNextStepFrame load]
objc[29649]: LOAD: class '_DKEventQuery' scheduled for +load
objc[29649]: LOAD: +[_DKEventQuery load]
在控制台里我们看到有好多load方法的打印,可以看到好多类实现了load方法。
下面是设置动态库的打印,设置环境变量为OBJC_PRINT_IMAGES,打印如下
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/GLKit.framework/Versions/A/GLKit (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/ModelIO.framework/Versions/A/ModelIO (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/PDFKit.framework/Versions/A/PDFKit (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/AVKit.framework/Versions/A/AVKit (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/SceneKit.framework/Versions/A/SceneKit (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory (has class properties) (preoptimized)
objc[29649]: IMAGES: loading image for /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji (has class properties) (preoptimized)
下面是一些xcode部分环境变量设置,在源码里开启help_帮助就可以打印出:
objc[29649]: Objective-C runtime debugging. Set variable=YES to enable.
objc[29649]: OBJC_HELP: describe available environment variables
objc[29649]: OBJC_HELP is set
objc[29649]: OBJC_PRINT_OPTIONS: list which options are set
objc[29649]: OBJC_PRINT_OPTIONS is set
objc[29649]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[29649]: OBJC_PRINT_IMAGES is set
objc[29649]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[29649]: OBJC_PRINT_IMAGE_TIMES is set
objc[29649]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[29649]: OBJC_PRINT_LOAD_METHODS is set
objc[29649]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[29649]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[29649]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[29649]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[29649]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[29649]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[29649]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
下面我们重点看下 _dyld_objc_notify_register(&map_images, load_images, unmap_image);这个方法里的map_images和load_images。&map_images是引用传递,load_images是值传递。
load_images方法就是去查找和调用load方法。load方法的调用顺序是 先找分类-->本类(子类)-->分类,如果两个分类都实现load方法,那就看编译的顺序,先编译就后执行
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
接着我们看map_images这个方法:
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
oid
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
}
if (PrintImages) {
_objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}
// Find all images with Objective-C metadata.
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
// Size some data structures based on main executable's size
#if __OBJC2__
// If dyld3 optimized the main executable, then there shouldn't
// be any selrefs needed in the dynamic map so we can just init
// to a 0 sized map
if ( !hi->hasPreoptimizedSelectors() ) {
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
}
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
其实在map_images这个方法里我们主要研究read_images里的方法,方法如下,下面方法是进行层层跳转过来的。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
bool launchTime = NO;
TimeLogger ts(PrintImageTimes);
runtimeLock.assertLocked();
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.
# if SUPPORT_INDEXED_ISA
// Disable nonpointer isa if any image contains old Swift code
for (EACH_HEADER) {
if (hi->info()->containsSwift() &&
hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app or a framework contains Swift code "
"older than Swift 3.0");
}
break;
}
}
网友评论