objc中load、initialize方法的调用

Objective-C中+load与+initialize方法详解 在Objective-C运行时环境中,类初始化方法是实现对象生命周期管理的重要机制。+load和+initialize作为两个特殊的类方法,分别处理不同的初始化场景。+load方法在类被加载到运行时环境时立即执行,适合进行Method Swizzling等早期配置工作;而+initialize采用惰性加载策略,在类首次使用时触发,常用于单例初始化和按需资源加载。理解这两种方法的调用时机、线程安全特性和执行顺序差异,对开发高性能iOS应用至关重要。特别是在组件化架构和模块化开发中,合理使用这些初始化方法能有效 阅读详情

1.load 的调用

结论


1.先调用类的 load 方法,先编译哪个类就先调用该类的 load.

2.在调用 load 之前调用父类 load 方法.

3.分类 load 方法不会覆盖本类的 load 方法.

1.准备工作

请添加图片描述


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 {
	    //  这里调用了  sNotifyObjCMapped 即 map_classes
		notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
	}
	catch (const char* msg) {
		// ignore request to abort during registration
	}

	// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
	for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
		ImageLoader* image = *it;
		if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
			dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
			(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
		}
	}
}


2.什么时候调用 sNotifyObjCMapped?

registerObjCNotifiers-> notifyBatch -> notifyBatchPartial-> map_images

堆栈完全印证


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1
  * frame #0: 0x000000010049ce23 libobjc.A.dylib`map_images(count=56, paths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40) at objc-runtime-new.mm:3159:24
    frame #1: 0x00007ff8001fc740 dyld`invocation function for block in dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*))::$_8::operator()() const + 637
    frame #2: 0x00007ff8001f46d9 dyld`dyld4::RuntimeState::withLoadersReadLock(void () block_pointer) + 41
    frame #3: 0x00007ff8001f95db dyld`dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*)) + 209
    frame #4: 0x00007ff80021f188 dyld`dyld4::APIs::_dyld_objc_register_callbacks(_dyld_objc_callbacks const*) + 138
    frame #5: 0x00000001004eecfd libobjc.A.dylib`_objc_init at objc-os.mm:815:5
    frame #6: 0x00000001000adfb9 libdispatch.dylib`_os_object_init + 13
    frame #7: 0x00000001000bfd9c libdispatch.dylib`libdispatch_init + 363
    frame #8: 0x00007ff80c3b1898 libSystem.B.dylib`libSystem_initializer + 238
    frame #9: 0x00007ff8002063fb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 175
    frame #10: 0x00007ff800244b7a dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
    frame #11: 0x00007ff800238f22 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 577
    frame #12: 0x00007ff8001e90af dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 245
    frame #13: 0x00007ff8002380bf dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 175
    frame #14: 0x00007ff80024473a dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 470
    frame #15: 0x00007ff80020366c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 220
    frame #16: 0x00007ff800224bdd dyld`dyld4::APIs::runAllInitializersForMain() + 65
    frame #17: 0x00007ff8001ee874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #18: 0x00007ff8001ed3bd dyld`start + 1805

3.什么时候调用 load 方法

_dyld_start -> dyldbootstrap::start -> dyld::_main -> initializeMainExecutable -> ImageLoader::runInitializers -> ImageLoader::processInitializers -> ImageLoader::recursiveInitialization -> notifySingle -> sNotifyObjCInit -> load_images -> +load


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
  * frame #0: 0x00000001000033d0 KCObjcBuild`+[LGSon load](self=LGSon, _cmd="load") at main.m:57:5 [opt]
    frame #1: 0x0000000100497026 libobjc.A.dylib`call_class_loads() at objc-loadmethod.mm:204:9
    frame #2: 0x0000000100496f0d libobjc.A.dylib`call_load_methods at objc-loadmethod.mm:353:13
    frame #3: 0x000000010049cf6d libobjc.A.dylib`load_images(path="/Users/lee/Library/Developer/Xcode/DerivedData/objc-ascfiajdmnkqzabgrazgkmriraio/Build/Products/Debug/KCObjcBuild", mh=0x0000000100000000) at objc-runtime-new.mm:3295:5
    frame #4: 0x00007ff8001f96d8 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
    frame #5: 0x00007ff80020384f dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 167
    frame #6: 0x00007ff80020646f dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_1::operator()() const + 97
    frame #7: 0x00007ff8002038f1 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 93
    frame #8: 0x00007ff800224cd2 dyld`dyld4::APIs::runAllInitializersForMain() + 310
    frame #9: 0x00007ff8001ee874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #10: 0x00007ff8001ed3bd dyld`start + 1805

堆栈印证

4.如何获取到类和对应的load 方法?

按照有序,将 需要被执行 load 的class 添加到 loadable_classes 中


static void schedule_class_load(Class cls)
{
    if (!cls) return;
    ASSERT(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->getSuperclass());

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED);
}



可以看到递归调用 schedule_class_load ,优先添加 父类,再添加子类


void add_class_to_loadable_list(Class cls)
{
    IMP method;

    lockdebug::assert_locked(&loadMethodLock);

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}




调用堆栈


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 10.1
  * frame #0: 0x0000000100496bfa libobjc.A.dylib`add_class_to_loadable_list(cls=0x00007ff843c3b278) at objc-loadmethod.mm:86:26
    frame #1: 0x000000010049f4a0 libobjc.A.dylib`schedule_class_load(cls=0x00007ff843c3b278) at objc-runtime-new.mm:3954:5
    frame #2: 0x000000010049d168 libobjc.A.dylib`prepare_load_methods(mhdr=0x00007ff80144e000) at objc-runtime-new.mm:3976:9
    frame #3: 0x000000010049cf5a libobjc.A.dylib`load_images(path="/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation", mh=0x00007ff80144e000) at objc-runtime-new.mm:3291:9
    frame #4: 0x00007ff8001f96d8 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
    frame #5: 0x00007ff80020384f dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 167
    frame #6: 0x00007ff80020383d dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 149
    frame #7: 0x00007ff80020646f dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_1::operator()() const + 97
    frame #8: 0x00007ff8002038f1 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 93
    frame #9: 0x00007ff800224cd2 dyld`dyld4::APIs::runAllInitializersForMain() + 310
    frame #10: 0x00007ff8001ee874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #11: 0x00007ff8001ed3bd dyld`start + 1805


再在 prepare_load_methods 方法中添加分类可加载的 class


void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    lockdebug::assert_locked(&runtimeLock);

    classref_t const *classlist =
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

5.load 方法的执行

在 call_class_loads 方法中,按序遍历 loadable_classes 数组


static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

所以有网上常说的


1.先调用类的 load 方法,先编译哪个类就先调用该类的 load.

2.在调用 load 之前调用父类 load 方法.

3.分类 load 方法不会覆盖本类的 load 方法.

2.load 的调用

结论


1.initialize 方法先初始化父类,之后再初始化子类.

2.如果子类未实现 initialize 方法,就会调用父类的 initialize 方法.

3.如果分类实现了 initialize 方法,会覆盖本类 initialize 方法.

1.为 NSObject 根类添加 initialize 方法

// 如果根类尚未实现,则会获得额外的方法实现。这些方法在类别替换之前生效。
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

调用堆栈


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 11.1
  * frame #0: 0x00000001004b99b5 libobjc.A.dylib`methodizeClass(cls=0x00000001005180f0, previously=0x0000000000000000) at objc-runtime-new.mm:1547:19
    frame #1: 0x000000010049c00e libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00000001005180f0, previously=0x0000000000000000) at objc-runtime-new.mm:2747:5
    frame #2: 0x000000010049bde5 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x0000000100518140, previously=0x0000000000000000) at objc-runtime-new.mm:2665:15
    frame #3: 0x000000010049bdc2 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x000000010011d700, previously=0x0000000000000000) at objc-runtime-new.mm:2664:16
    frame #4: 0x000000010049bdc2 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00007ff843c221f0, previously=0x0000000000000000) at objc-runtime-new.mm:2664:16
    frame #5: 0x000000010049bdc2 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00007ff843c22740, previously=0x0000000000000000) at objc-runtime-new.mm:2664:16    frame #6: 0x000000010049e21a libobjc.A.dylib`_read_images(hList=0x00007ff7bfefb3d0, hCount=56, totalClasses=2149, unoptimizedTotalClasses=2149) at objc-runtime-new.mm:3852:13
    frame #7: 0x00000001004edb38 libobjc.A.dylib`map_images_nolock(mhCount=56, mhPaths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40, disabledClassROEnforcement=0x00007ff7bfefb6e7) at objc-os.mm:470:9
    frame #8: 0x000000010049ce61 libobjc.A.dylib`map_images(count=56, paths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40) at objc-runtime-new.mm:3165:9
    frame #9: 0x00007ff8001fc740 dyld`invocation function for block in dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*))::$_8::operator()() const + 637
    frame #10: 0x00007ff8001f46d9 dyld`dyld4::RuntimeState::withLoadersReadLock(void () block_pointer) + 41
    frame #11: 0x00007ff8001f95db dyld`dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*)) + 209
    frame #12: 0x00007ff80021f188 dyld`dyld4::APIs::_dyld_objc_register_callbacks(_dyld_objc_callbacks const*) + 138
    frame #13: 0x00000001004eecfd libobjc.A.dylib`_objc_init at objc-os.mm:815:5
    frame #14: 0x00000001000adfb9 libdispatch.dylib`_os_object_init + 13
    frame #15: 0x00000001000bfd9c libdispatch.dylib`libdispatch_init + 363
    frame #16: 0x00007ff80c3b1898 libSystem.B.dylib`libSystem_initializer + 238
    frame #17: 0x00007ff8002063fb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 175
    frame #18: 0x00007ff800244b7a dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
    frame #19: 0x00007ff800238f22 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 577
    frame #20: 0x00007ff8001e90af dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 245
    frame #21: 0x00007ff8002380bf dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 175
    frame #22: 0x00007ff80024473a dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 470
    frame #23: 0x00007ff80020366c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 220
    frame #24: 0x00007ff800224bdd dyld`dyld4::APIs::runAllInitializersForMain() + 65
    frame #25: 0x00007ff8001ee874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #26: 0x00007ff8001ed3bd dyld`start + 1805

2.先调用父类 initialize 方法


void initializeNonMetaClass(Class cls)
{
    ASSERT(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->getSuperclass();
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
     ...
     }

调用堆栈


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.1
    frame #0: 0x00000001000030f0 KCObjcBuild`+[LGPerson initialize](self=LGPerson, _cmd="initialize") at main.m:43:5 [opt]
    frame #1: 0x000000010049236c libobjc.A.dylib`_CALLING_SOME_+initialize_METHOD(cls=LGPerson) at objc-initialize.mm:384:5
    frame #2: 0x00000001004928b1 libobjc.A.dylib`initializeNonMetaClass(cls=LGPerson) at objc-initialize.mm:549:13
    frame #3: 0x00000001004926c3 libobjc.A.dylib`initializeNonMetaClass(cls=LGSon) at objc-initialize.mm:505:9
    frame #4: 0x000000010049b51e libobjc.A.dylib`initializeAndMaybeRelock(cls=0x0000000100008720, inst=0x0000000100008748, lock=0x000000010051b0c0, leaveLocked=true) at objc-runtime-new.mm:2220:5
    frame #5: 0x00000001004cb25a libobjc.A.dylib`initializeAndLeaveLocked(cls=0x0000000100008720, obj=0x0000000100008748, lock=0x000000010051b0c0) at objc-runtime-new.mm:2236:12
    frame #6: 0x00000001004ac2d0 libobjc.A.dylib`realizeAndInitializeIfNeeded_locked(inst=0x0000000100008748, cls=0x0000000100008720, initialize=true) at objc-runtime-new.mm:6769:15
    frame #7: 0x00000001004abd5d libobjc.A.dylib`lookUpImpOrForward(inst=0x0000000100008748, sel="alloc", cls=0x0000000100008720, behavior=11) at objc-runtime-new.mm:6886:11
    frame #8: 0x00000001004e77db libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #9: 0x00000001004fc554 libobjc.A.dylib`objc_alloc_init [inlined] callAlloc(cls=LGSon, checkNil=true, allocWithZone=false) at NSObject.mm:2011:12
    frame #10: 0x00000001004fc4ac libobjc.A.dylib`objc_alloc_init(cls=LGSon) at NSObject.mm:2041:13
    frame #11: 0x0000000100003a8d KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:207:24 [opt]
    frame #12: 0x00007ff8001ed41f dyld`start + 1903




3.再调用子类 initialize 方法

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}

void initializeNonMetaClass(Class cls)
{
    ASSERT(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->getSuperclass();
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;

            // Grab a copy of the will-initialize funcs with the lock held.
            localWillInitializeFuncs.initFrom(willInitializeFuncs);
        }
    }
    
    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }
        
        for (auto callback : localWillInitializeFuncs)
            callback.f(callback.context, cls);

        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                         objc_thread_self(), cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        //
        @try
        {
            callInitialize(cls);
            }
            ...
}

调用堆栈


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1
    frame #0: 0x00000001000033c0 KCObjcBuild`+[LGSon initialize](self=LGSon, _cmd="initialize") at main.m:61:5 [opt]
    frame #1: 0x000000010049236c libobjc.A.dylib`_CALLING_SOME_+initialize_METHOD(cls=LGSon) at objc-initialize.mm:384:5
    frame #2: 0x00000001004928b1 libobjc.A.dylib`initializeNonMetaClass(cls=LGSon) at objc-initialize.mm:549:13
    frame #3: 0x000000010049b51e libobjc.A.dylib`initializeAndMaybeRelock(cls=0x0000000100008708, inst=0x0000000100008730, lock=0x000000010051b0c0, leaveLocked=true) at objc-runtime-new.mm:2220:5
    frame #4: 0x00000001004cb25a libobjc.A.dylib`initializeAndLeaveLocked(cls=0x0000000100008708, obj=0x0000000100008730, lock=0x000000010051b0c0) at objc-runtime-new.mm:2236:12
    frame #5: 0x00000001004ac2d0 libobjc.A.dylib`realizeAndInitializeIfNeeded_locked(inst=0x0000000100008730, cls=0x0000000100008708, initialize=true) at objc-runtime-new.mm:6769:15
    frame #6: 0x00000001004abd5d libobjc.A.dylib`lookUpImpOrForward(inst=0x0000000100008730, sel="alloc", cls=0x0000000100008708, behavior=11) at objc-runtime-new.mm:6886:11
    frame #7: 0x00000001004e77db libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #8: 0x00000001004fc554 libobjc.A.dylib`objc_alloc_init [inlined] callAlloc(cls=LGSon, checkNil=true, allocWithZone=false) at NSObject.mm:2011:12
    frame #9: 0x00000001004fc4ac libobjc.A.dylib`objc_alloc_init(cls=LGSon) at NSObject.mm:2041:13
    frame #10: 0x0000000100003a8d KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:202:24 [opt]
    frame #11: 0x00007ff8001ed41f dyld`start + 1903

4.分类 initialize 方法会覆盖当前类initialize 方法

调用堆栈和 3.再调用子类 initialize 方法 完全一致,说明当前类的 initialize 方法被分类代替

initialize也只是一个普通的方法,符合下面调用特征

1.当分类、原来类、原来类的父类中有相同方法时,方法调用的优先级:分类(最后参与编译的分类优先) –> 原来类 –> 父类,即先去调用分类中的方法,分类中没这个方法再去原来类中找,原来类中没有再去父类中找。

2.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用(实际上并没有真的替换,而是Category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的Category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法)。

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}

void initializeNonMetaClass(Class cls)
{
    ASSERT(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->getSuperclass();
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;

            // Grab a copy of the will-initialize funcs with the lock held.
            localWillInitializeFuncs.initFrom(willInitializeFuncs);
        }
    }
    
    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }
        
        for (auto callback : localWillInitializeFuncs)
            callback.f(callback.context, cls);

        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                         objc_thread_self(), cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        //
        @try
        {
            callInitialize(cls);
            }
            ...
}

调用堆栈

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.1
  * frame #0: 0x00000001000036c0 KCObjcBuild`+[LGSon(self=LGSon, _cmd="initialize") initialize] at main.m:127:5 [opt]
    frame #1: 0x000000010049236c libobjc.A.dylib`_CALLING_SOME_+initialize_METHOD(cls=LGSon) at objc-initialize.mm:384:5
    frame #2: 0x00000001004928b1 libobjc.A.dylib`initializeNonMetaClass(cls=LGSon) at objc-initialize.mm:549:13
    frame #3: 0x000000010049b51e libobjc.A.dylib`initializeAndMaybeRelock(cls=0x0000000100008798, inst=0x00000001000087c0, lock=0x000000010051b0c0, leaveLocked=true) at objc-runtime-new.mm:2220:5
    frame #4: 0x00000001004cb25a libobjc.A.dylib`initializeAndLeaveLocked(cls=0x0000000100008798, obj=0x00000001000087c0, lock=0x000000010051b0c0) at objc-runtime-new.mm:2236:12
    frame #5: 0x00000001004ac2d0 libobjc.A.dylib`realizeAndInitializeIfNeeded_locked(inst=0x00000001000087c0, cls=0x0000000100008798, initialize=true) at objc-runtime-new.mm:6769:15
    frame #6: 0x00000001004abd5d libobjc.A.dylib`lookUpImpOrForward(inst=0x00000001000087c0, sel="alloc", cls=0x0000000100008798, behavior=11) at objc-runtime-new.mm:6886:11
    frame #7: 0x00000001004e77db libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #8: 0x00000001004fc554 libobjc.A.dylib`objc_alloc_init [inlined] callAlloc(cls=LGSon, checkNil=true, allocWithZone=false) at NSObject.mm:2011:12
    frame #9: 0x00000001004fc4ac libobjc.A.dylib`objc_alloc_init(cls=LGSon) at NSObject.mm:2041:13
    frame #10: 0x0000000100003a8d KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:223:24 [opt]
    frame #11: 0x00007ff8001ed41f dyld`start + 1903

所以有网上常说的


1.initialize 方法先初始化父类,之后再初始化子类.

2.如果子类未实现 initialize 方法,就会调用父类的 initialize 方法.

3.如果分类实现了 initialize 方法,会覆盖本类 initialize 方法.

从零搭建云手机:基于Docker与KVM的实战指南 本文提供了一份基于Docker与KVM从零搭建私有云手机的实战指南。详细介绍了服务器配置选择、KVM虚拟化环境检查、Docker容器部署以及通过VNC进行远程访问的全过程。该方案轻量高效,适合应用测试、手游多开及自动化脚本运行等场景,帮助用户低成本拥有高性能云端安卓设备。 阅读详情

相关推荐

一分钟教会你搭建steam新游幻兽帕鲁服务器

总结: 游戏对服务器搭建要求相对较高、且对服务器网络协议有要求不能封堵UDP、多开、游戏运行久、同时在线人数多了这些会很吃内存 我自己用的64G内存多开4区测试玩家同时在线50多人的情况下内存已经跑满了、对于想用服务器搭建联机的小伙伴建议根据开区数量以及玩家人数去选择配置、当然也可以直接找我阿杰购买、我可以提供技术搭建服务。注:如果用的是本地电脑局域网搭建直接输入127.0.0.1:8211就行 如果用的是服务器搭建输入自己服务器IP+端口或解析的域名+端口进入游戏。

用最简单的方式说最重要的事!服务器、存储、云计 算、A智能的大数据产品、游戏开发技术。致力于广 大开发者、政企用户、各项机构等,构建云上世界提供全方位云计算解决方案。旨在打造差异化的开放式闭环生态系统帮助用户快速构建稳定、安全的云计算环境。 2235

ObjLoader 项目常见问题解决方案

ObjLoader 项目常见问题解决方案 项目基础介绍 ObjLoader 是一个用于加载 Wavefront .obj 和 .mtl 文件格式的开源库,它是由 C# 编程语言编写的。该库可以帮助开发者轻松地将 3D 模型加载到他们的应用程序中。 主要编程语言 C# 新手常见问题及解决步骤 问题一:如何正确安装和引用项目 问题描述:新手用户在尝试使用 ObjLoader 时,不知道如何安装和引用这...

gitblog_00331的博客 370

腾讯会议视频过期了怎么办?亲测可用:用这个免费插件抢救你的会议录制与回放

本文提供了三种亲测有效的腾讯会议视频过期恢复方案,包括浏览器缓存提取、专业工具组合及预防性措施。特别推荐使用免费插件抢救过期会议录制与回放,帮助职场人解决重要视频资料丢失的难题。

weixin_33686714的博客 264

iOS:底层原理之 +load & +initialize

+load 方法 +load 方法会在 runtime 加载类、分类的时候调用 每个类、分类的 +load,在程序运行过程中只调用一次 可以理解为加载入内存时调用。 调用顺序 先调用类的 +load 按照编译先后顺序调用(先编译,先调用) 调用子类的 +load 之前会先调用父类的 +load 再调用分类的 +load 按照编译顺序调用(先编译,先调用) objc4 源码解读过...

GSNICE 678

Unity URP项目里Post Processing死活没效果?别慌,这5个坑我帮你踩过了

本文详细解析了Unity URP项目中Post Processing效果失效的五大常见问题,包括渲染管线配置冲突、Volume作用域误判、摄像机参数漏配、Layer层级冲突和Profile引用异常。通过实际项目案例和代码示例,提供专业级解决方案,帮助开发者快速排查并修复后处理效果不生效的问题。

weixin_33721427的博客 333

Multisim仿真电路:(十二)MOS管缓启动电路设计与优化

本文详细介绍了基于Multisim的MOS管缓启动电路设计与优化方法。通过分析米勒平台效应,探讨RC参数选择、多级设计和有源电路等技巧,有效抑制冲击电流并优化开关损耗。文章结合仿真实例,提供实用的设计策略和实际应用注意事项,帮助工程师提升电源管理系统的可靠性。

pytorch8learner的博客 237

MTK相机启动流程拆解:从Camera HAL到Sensor驱动的完整链路

相机启动是Android影像系统中链路最长、最易出问题的环节之一。从应用层调用openCamera到预览首帧上屏,中间跨越CameraService、Camera HAL、Pipeline、ISP与Sensor驱动等多个层级,每个环节都涉及状态机、Buffer管理和异步回调。理解Camera HAL3模型下configureStreams与processCaptureRequest的分工协作,是掌握整条启动链路的关键。Pipeline作为数据流的中枢,通过P1、P2A/P2B等节点完成从RAW数据到可显示图

weixin_34288121的博客 308

当LightGBM推理遇上GPU:3种替代方案性能对比(含ONNX实战代码)

本文针对LightGBM官方不支持GPU推理的性能瓶颈,深度评测了RAPIDS cuML、ONNX Runtime GPU和Triton Inference Server三种GPU加速推理替代方案。通过详尽的性能对比和实战代码,为高并发、低延迟的在线预测场景提供了清晰的选型指南,帮助工程师打破推理性能枷锁,实现显著的吞吐量提升。

salt9的博客 813

iOS load和initialize区别

区别: 1、调用时刻: load在main函数之前调用,initialize在main函数之后调用。所有类的load方法都只调用一次。 (1)load是runtime加载类、分类的时候调用(只会调用1次) 主类与分类的加载顺序:先加载所有主类,再加载所有分类。 加载主类时,父类和子类的加载顺序:先加载父类,再加载子类。与编译顺序无关。 加载分类时,先编译先加载,后编译后加载。与编译顺序...

ihoudf 580

构建AI Agent驱动的智能健康管理系统

数据碎片化:健康数据分散在不同的设备和平台中,难以统一管理和分析。个性化不足:健康管理方案通常是通用的,难以根据个人的健康状况实时调整。用户参与度低:用户难以获得及时、个性化的健康建议,导致健康管理效果不佳。AI Agent是一个能够感知环境、采取行动以实现目标的智能实体。根据智能体的智能水平,可以分为简单反应式智能体和复杂适应式智能体。实时监测健康数据。获取个性化的健康建议。收到健康风险预警。

AI天才研究院 749

【PCIe硬件】PCIe引脚PRSNT与热插拔

热插拔的基本目的是要让PCIe设备按照规定的顺序、原则,从系统中移除或插入到系统中来,并能正常的工作,且不影响系统的正常运行。事实上,PCIe“热插拔”的关键目的就是为前面面所提到的系统RAS服务的,是提升系统RAS能力的非常重要的手段!注意,这里有两个相关概念,前面我们讲的热插拔,其实是广义的“热插拔”。这里的Hot-Plug,为了方便,我们叫做PCIe热插拔,也就是我们现在及后面将要讨论的内容。

smartvxworks的博客 2804

08-load、initialize的内容相关

load方法的调用方式是直接通过指针寻找,并非常规的方法调用方式(消息发送机制).所以不存在方法覆盖问题.这一点可以通过源码中。方法是runtime在加载类、分类时自动调用的方法.关于 load 方法的调用顺序主要有以下三点.调用时机是类第一次调用方法时调用的方法.,所以方法调用顺序以及调用过程按照。. 调用顺序可以查看源码中得。的调用顺序是先调用父类的。注: 本问题回答基于。这两个方法进行确认.

得不到的永远是骚栋. 371

哨兵1号后向散射系数土壤水分反演

打开之前预处理之后的VH和VV极化的后向散射系数转存的tiff文件。

所有专家都曾始于新手 3292

【iOS】类和分类的加载过程

本文深入分析了Objective-C中类和分类的加载流程,主要从_objc_init方法入手,探讨了dyld与Objc的关联机制以及类和分类的加载细节。文章首先介绍了_objc_init中的关键初始化方法,包括环境变量设置、线程初始化等。随后重点分析了map_images和load_images的调用时机和功能:map_images负责将Mach-O中的类信息加载到内存,而load_images则处理+load方法的调用。在类加载部分,详细解读了readClass和realizeClassWithou...

2301_80095702的博客 876

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Category、load、initialize、Class、关联对象、isa

Category的实现原理 Category编译之后的底层结构是struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息。在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中) Category和Class Extension的区别是什么? Class Extension在编译的时候,它的数据就已经包含在类信息中 Categor...

qq_35544470的博客 522

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

iOS开发~Runtime学习总结

1、Runtime的内存模型(isa、对象、类、metaclass、结构体的存储信息等) 对象:OC中的对象指向的是一个objc_object指针类型,typedef struct objc_object *id;从它的结构体中可以看出,它包括一个isa指针,指向的是这个对象的类对象,一个对象实例就是通过这个isa找到它自己的Class,而这个Class中存储的就是这个实例的方法列表、属性列表、成员变量列表等相关信息的。 /// Represents an instance of a class. s

阿福的专栏 750

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

iOS +load、+ initialize、init介绍

如果子类没有实现+initialize,那么就是先调用一次父类的+initialize,然后根据objc_msgSend的特点,在子类找不到+initialize,会到父类去找,那么就会再调用一次父类的+initialize方法,总共调用两次。2、如果分类实现了+initialize,则会覆盖类本身的+initialize调用,且调用顺序还是先调用父类分类的+initialize,再调用子类分类的+initialize;2、子类调用+load方法时,要先要调用父类的+load方法;

liuxinzhongliubin的博客 1322

load 和 initialize

load ,initialize 有啥区别 load 是类在加载到内存的时候 被调用的 如果特别想在这个时候做什么可以调用 直入主题上代码 按照分类的说法应该是在分类中的方法最先调用的才对。但是现在每一个都有调用这是为啥? 直接入手runtime源码 objc-os.mm文件 runtime初始化方法入手 load_images 名字可见是要找的东西 jump进去 load_images(co...

annchilde的专栏 298

Load和initialize方法

1、load方法 load方法会在runtime加载类、分类时调用(在main函数开始之前),与这个类是否用到无关。 类、分类中的load方法 从苹果源码网站下载下来runtime的objc4-723源码。 obje-runtime-new.mm文件中有个load_images方法指明加载文件到内存中,其中call_load_methods这句代码便对应load方法的处理: 这里可以看出是先调用...

weixin_33834137的博客 224

NSObject的load和initialize方法

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34391854的博客 76

XB_Category(2)

1.+(void)load load方法在runtime加载类、分类的时候调用,不管是否使用了这个类,都会调用load方法,并且在程序运行过程中只调用一次, 1>load方法也会随着Category合并到元类中的方法列表中,所以如果手动调用load方法也是遵循obj c_msgSend消息发送机制的比如[NSPersonload],如果没有实现,会调用父类的load方法实现。 2&...

HuberCui的博客 408

分类(Category)的本质 及其与类扩展(Extension) /继承(Inherit)的区别

1、分类的概念 分类是为了扩展系统类的方法而产生的一种方式,其作用就是在不修改原有类的基础上,为一个类扩展方法,最主要的是可以给系统类扩展我们自己定义的方法。 如何创建一个分类?↓↓ (1)Cmd+N,iOS-->Objective-C File,Next; (2)File Type选择category,class选择需要的类,分类名,Next。 比如我们为Person创建...

weixin_30725315的博客 926

移动端开发iOS系列之Runtime

作者:洋仔 链接:https://juejin.cn/post/6961653605652758564 1.objc在向一个对象发送消息时,发生了什么? objc在向一个对象发送消息时,runtime会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果一直到根类还没找到,转向拦截调用,走消息转发机制,一旦找到 ,就去执行它的实现IMP 。 2.objc中向一个nil对象发送消息将会发生什么? 如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是

henry_lei的博客 459

iOS进阶 - Runtime

1.Category 的实现原理? Category 实际上是 Category_t的结构体,在运行时,新添加的方法,都被以倒序插入到原有方法列表的最前面,所以不同的Category,添加了同一个方法,执行的实际上是最后一个。 Category 在刚刚编译完的时候,和原来的类是分开的,只有在程序运行起来后,通过 Runtime ,Category 和原来的类才会合并到一起。 2.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型? isa 等价于 is kind o

hiccuptang 225

ObjC load与initialize 简析

+ (void)load 每个类,每个分类中都存在一个+(void)load方法。我们不需要显式的进行调用,当runtime动态加载类、分类的时候会进行调用。 创建一个command line 项目 创建几个类。Student继承自Person,每个类包括其Category中都实现load方法,运行我们发现控制台打印了以下信息。 我们发现类中的load方法没有被Category中的load...

weixin_34314962的博客 193

OC对象 - +load、initialize方法

initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点。从类文件列表看到,ZSXPerson在其两个分类的后面编译的,但是它的+load始终是最先调用的。走了runtime机制调用,我们知道,子类收到消息时,如果自身找不到方法实现,会到父类中继续查找。+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用。发送消息,因为分类方法覆盖了类的方法,会先执行父类分类。注:如果有分类的情况,发送消息,先执行父类。

sharp521的博客 862
上一篇: objc-类和对象地址的确定
滑板小王子
博客等级 码龄9年 19粉丝 25原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值