精华内容
下载资源
问答
  • 之前编译安装的LNMP环境+phpmyamdin4.02的版本,今天突然出现这个问题:复制代码 代码如下:Fatal error: session_start(): Failed to initialize storage module: files (path: ) in /data/...第一反应就是查看php.ini...
  • 方法'initialize()'定义了Objective-C类的方法'initialize',它不能保证由Swift调用,并且在以后的版本中将不允许使用。 要求 iOS 8.0以上 迅捷3.0+ 用法 首先,将Sources文件夹下的文件拖放到您的项目中。 然后...
  • 主要介绍了Plsql Developer连接Oracle时出现Could not initialize oci.dll解决方案,需要的朋友可以参考下
  • 主要介绍了ThinkPHP中__initialize()和类的构造函数__construct()用法,以实例形式分析了ThinkPHP中类的初始化时构造子类的方法,是采用ThinkPHP进行面向对象程序设计中比较重要的概念,需要的朋友可以参考下
  • NULL 博文链接:https://justsee.iteye.com/blog/1630979
  • This is probably because cuDNN failed to initialize, so try…”于是打开retrain.py文件,在前面加上如下代码: from tensorflow.compat.v1 import ConfigProto from tensorflow.compat.v1 import ...
  • This is probably because cuDNN failed to initialize, so try looking to see if a warning log message was printed above. 主要原因是版本问题,还有可能是没有安装cuda / cudnn ,具体可在pycharm的设置中查看
  • 主要介绍了thinkPHP中_initialize方法,结合实例形式分析了子类调用父类_initialize方法的原理与相关操作技巧,需要的朋友可以参考下
  • jquery.initialize 1.4.0 1.4.0 之前的最新更新:添加了子元素数据绑定。 最新更新至 1.3.0:数据属性、模板属性和字符串模板替换。 一个简单的 jQuery 元素初始值设定项上下文插件。 该插件使元素初始化并将定义...
  • NULL 博文链接:https://smartgwt.iteye.com/blog/1252268
  • 解决:oracle_plsql连服务端时 Initialization error could initialize错误处理方法
  • NULL 博文链接:https://fkshl.iteye.com/blog/1178391
  • initialize_linux-源码

    2021-02-23 01:51:13
    initialize_linux Shell脚本可初始化ubuntu的安装。
  • 像pattr_initialize (或attr_private_initialize )和method_object这样的更高级别的方法也可以真正减少样板。 代替 class InvoicePolicy def initialize ( invoice , company : ) @invoice = invoice @...
  • gem 'better-initialize', github: 'huned/better-initialize' 代码: require 'better_initialize' class Pizza include BetterInitialize attr_accessor :size, :toppings end # Instantiate with ...
  • RGWRados initialize.pdf

    2019-09-17 22:45:52
    RGWRados::initialize 框架 待完善 代码流程,需要进一步的梳理流程
  • Ajax-jquery.initialize.zip

    2019-09-17 10:41:31
    Ajax-jquery.initialize.zip,用于动态创建元素初始化的jquery插件(几年前很好,在2019年 考虑react或其他东西而不是jquery),ajax代表异步javascript和xml。它是多种web技术的集合,包括html、css、json、xml和...
  • Laravel开发-laravel-initialize Laravel初始化
  • 演示如何使用设计器将具有唯一名称的子控件预填充自定义Windows控件。
  • System-Initialize、Finalize.mht
  • initialize方法

    千次阅读 2020-03-07 21:15:19
    1、+ initialize方法会在类第一次接受到消息时调用 2、+initialize方法是通过objc_msgSend函数(消息发送机制)调用 2.1、如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用...

    1、+ initialize方法会在类第一次接受到消息时调用

    2、+initialize方法是通过objc_msgSend函数(消息发送机制)调用

    2.1、如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
    2.2、如果分类实现了+initialize,会覆盖类本身的+initialize的调用

    调用顺序

    1、先调用父类的+initialize方法,再调用子类的+initialize方法(如果有实现+initialize时) — 先初始化父类,再初始化子类,每个类只会初始化一次

    2、遵循消息发送机制的方法查找顺序

    ============================== 源码解读顺序 ==============================
    /*
    	1、lookUpImpOrForward
    	2、initializeAndLeaveLocked
    	3、initializeAndMaybeRelock
    	4、initializeNonMetaClass
    	5、callInitialize
    	6、objc_msgSend
    */
    ============================== 核心代码查看 ==============================
    //	内部也是调用 class_getInstanceMethod方法
    Method class_getClassMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
        return class_getInstanceMethod(cls->getMeta(), sel);
    }
    
    /*
    	class_getInstanceMethod方法的实现
    	接下来查看 lookUpImpOrForward方法的实现
    */	
    Method class_getInstanceMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    #warning fixme build and search caches
        // Search method lists, try method resolver, etc.
        lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
    #warning fixme build and search caches
        return _class_getMethod(cls, sel);
    }
    
    /*
    	if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
    	标识需要初始化,并且这个类是还没有被初始化的
    	接下来查看 cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
    */
    MP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        const IMP forward_imp = (IMP)_objc_msgForward_impcache;
        IMP imp = nil;
        Class curClass;
        runtimeLock.assertUnlocked();
        // Optimistic cache lookup
        if (fastpath(behavior & LOOKUP_CACHE)) {
            imp = cache_getImp(cls, sel);
            if (imp) goto done_nolock;
        }
        runtimeLock.lock();
        checkIsKnownClass(cls);
        if (slowpath(!cls->isRealized())) {
            cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        }
        if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
            cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        }
        /*
    		省略其他代码
    	*/
    	return imp;
    }
    
    static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
    {
        return initializeAndMaybeRelock(cls, obj, lock, true);
    }
    
    //	查看initializeNonMetaClass(nonmeta)方法
    static Class initializeAndMaybeRelock(Class cls, id inst,
                                          mutex_t& lock, bool leaveLocked)
    {
        lock.assertLocked();
        ASSERT(cls->isRealized());
    
        if (cls->isInitialized()) {
            if (!leaveLocked) lock.unlock();
            return cls;
        }
        Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        if (nonmeta->isRealized()) {
            lock.unlock();
        } else {
            nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
            cls = object_getClass(nonmeta);
        }
        ASSERT(nonmeta->isRealized());
        initializeNonMetaClass(nonmeta);
    
        if (leaveLocked) runtimeLock.lock();
        return cls;
    }
    
    /*
    	if (supercls  &&  !supercls->isInitialized())
    	表示父类存在,且没有进行过初始化
    	
    	initializeNonMetaClass(supercls);
    	递归调用,此处可以说明会先调用父类的initialize方法
    	
    	接下来查看 callInitialize(cls);
    */
    void initializeNonMetaClass(Class cls)
    {
        ASSERT(!cls->isMetaClass());
    
        Class supercls;
        bool reallyInitialize = NO;
    
        supercls = cls->superclass;
        if (supercls  &&  !supercls->isInitialized()) {
            initializeNonMetaClass(supercls);
        }
        SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
        {
            monitor_locker_t lock(classInitLock);
            if (!cls->isInitialized() && !cls->isInitializing()) {
                cls->setInitializing();
                reallyInitialize = YES;
                localWillInitializeFuncs.initFrom(willInitializeFuncs);
            }
        }
        
        if (reallyInitialize) {
            _setThisThreadIsInitializingClass(cls);
    
            if (MultithreadedForkChild) {
                performForkChildInitialize(cls, supercls);
                return;
            }
            
            for (auto callback : localWillInitializeFuncs)
                callback.f(callback.context, cls);
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                             objc_thread_self(), cls->nameForLogging());
            }
    #if __OBJC2__
            @try
    #endif
            {
                callInitialize(cls);
                
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                                 objc_thread_self(), cls->nameForLogging());
                }
            }
    #if __OBJC2__
            @catch (...) {
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                                 "threw an exception",
                                 objc_thread_self(), cls->nameForLogging());
                }
                @throw;
            }
            @finally
    #endif
            {
                lockAndFinishInitializing(cls, supercls);
            }
            return;
        }
        
        else if (cls->isInitializing()) {
            if (_thisThreadIsInitializingClass(cls)) {
                return;
            } else if (!MultithreadedForkChild) {
                waitForInitializeToComplete(cls);
                return;
            } else {
                _setThisThreadIsInitializingClass(cls);
                performForkChildInitialize(cls, supercls);
            }
        }
        
        else if (cls->isInitialized()) {
            return;
        }
        else {
            _objc_fatal("thread-safe class init in objc runtime is buggy!");
        }
    }
    
    //	objc_msgSend方式调用SEL_initialize
    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
        asm("");
    }
    
    
    PS 此文为学习 李明杰 老师的 iOS底层原理课程所写笔记
    展开全文
  • 关于mac上运行kinect后出现Target VM failed to initialize的解决办法
  • +initialize方法的调用时机

    千次阅读 2019-01-23 16:04:44
    +initialize方法的调用时机 一个类或者它的子类收到第一条消息(手写代码调用,+load方法不算)之前调用,可以做一些初始化的工作。但该类的+initialize的方法调用,在其父类之后。 Runtime运行时以线程安全的方式...

    +initialize方法的调用时机

    一个类或者它的子类收到第一条消息(手写代码调用,+load方法不算)之前调用,可以做一些初始化的工作。但该类的+initialize的方法调用,在其父类之后。

    Runtime运行时以线程安全的方式将+initialize消息发送给类。也就是说,当一个类首次要执行手动调用的代码之前,会等待+initialize方法执行完毕后,再调用该方法。

    这里需要注意的一点:

    当子类没有实现+initialize或者子类在+initialize中显式的调用了[super initialize],那么父类的+initialize方法会被调用多次。如果希望避免某一个类中的+initialize方法被调用过多次,可以使用下面的方法来实现:

    + (void)initialize {
      if (self == [ClassName self]) {
        // ... do the initialization ...
      }
    }
    

    因为+initialize是以阻塞方式调用的,所以很重要的一点就是将方法实现限制为可能最小的工作量。

    Demo演示

    接下来我们写一个demo来验证一下,首先创建一个Person类,和Person的两个分类Test1,Test2。

    @interface Person : NSObject
    
    @end
    
    @implementation Person
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    
    
    @interface Person (Test1)
    
    @end
    
    @implementation Person (Test1)
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    
    
    @interface Person (Test2)
    
    @end
    
    @implementation Person (Test2)
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    

    然后新建一个Student类继承Person类,并实现Student的两个分类Test1,Test2。

    @interface Student : Person
    
    @end
    
    @implementation Student
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    
    
    @interface Student (Test1)
    
    @end
    
    @implementation Student (Test1)
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    
    
    @interface Student (Test2)
    
    @end
    
    @implementation Student (Test2)
    
    + (void)initialize
    {
        NSLog(@"---- %p %s", self, __FUNCTION__);
    }
    
    @end
    

    然后我们什么都不调用,直接运行程序,那应该也不会打印任何信息。

    然后,我们调用[Person alloc]方法,再次运行,查看结果。

    ---- 0x10f53ef60 +[Person(Test2) initialize]
    

    这里的输出分类的+initialize方法,查看一下Compile Sources,发现Person(Test2)分类在最后编译,所以调用的是它实现的+initialize方法。

    image

    这里,我们修改下代码,调用[Student alloc],然后看下运行结果。

    ---- 0x10c08af60 +[Person(Test2) initialize]
    ---- 0x10c08afb0 +[Student(Test1) initialize]
    

    这里发现,Student调用+initialize方法时是会先调用父类的+initialize方法(如果代码中没有写父类的调用代码)。

    然后我们继续新建一个HighSchoolStudent继承Student,什么都不实现,同时调用[HighSchoolStudent alloc],查看结果。

    ---- 0x101070048 +[Person(Test2) initialize]
    ---- 0x101070098 +[Student(Test1) initialize]
    ---- 0x10106fff8 +[Student(Test1) initialize]
    

    确实这样会调用两次+[Student(Test1) initialize]方法。

    [HighSchoolStudent initialize]的调用,是该类对象通过isa指针找到元类对象,在元类已缓存的方法列表中查方法,如果有就调用;如果没有就查找方法列表,如果还没有找到,那么就去类的父类的元类对象中查找,找到就调用。如果没有就递归下去直到NSObject的元类对象。所以找到了Student的元类对象,执行了initialize方法。

    源码分析

    我们可以在Runtime的源码中,分析一下调用+initialize方法的实现。首先一个类要调用方法时肯定是调用类方法,那么就先从class_getInstanceMethod方法入手查看吧。

    /***********************************************************************
    * class_getInstanceMethod.  Return the instance method for the
    * specified class and selector.
    **********************************************************************/
    Method class_getInstanceMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    
        // This deliberately avoids +initialize because it historically did so.
    
        // This implementation is a bit weird because it's the only place that 
        // wants a Method instead of an IMP.
    
    #warning fixme build and search caches
            
        // Search method lists, try method resolver, etc.
        lookUpImpOrNil(cls, sel, nil, 
                       NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
    
    #warning fixme build and search caches
    
        return _class_getMethod(cls, sel);
    }
    

    这里可以看到基本上就是调用了lookUpImpOrNil函数,而且函数列表中有注释标记了第四个参数/*initalize*/。那我们继续看。

    /***********************************************************************
    * lookUpImpOrNil.
    * Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
    **********************************************************************/
    IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
    {
        IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
        if (imp == _objc_msgForward_impcache) return nil;
        else return imp;
    }
    

    然后进入lookUpImpOrForward函数中继续追踪,该方法有些长,只截取了有关的部分代码。

    /***********************************************************************
    * lookUpImpOrForward.
    * The standard IMP lookup. 
    * initialize==NO tries to avoid +initialize (but sometimes fails)
    * cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
    * Most callers should use initialize==YES and cache==YES.
    * inst is an instance of cls or a subclass thereof, or nil if none is known. 
    *   If cls is an un-initialized metaclass then a non-nil inst is faster.
    * May return _objc_msgForward_impcache. IMPs destined for external use 
    *   must be converted to _objc_msgForward or _objc_msgForward_stret.
    *   If you don't want forwarding at all, use lookUpImpOrNil() instead.
    **********************************************************************/
    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        //  other code ..
    
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
            // If sel == initialize, _class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
    
        //  other code ..
    }
    

    这里判断如果需要初始化并且该类没有初始化,那么就进行类初始化。我们发现,我们从入口到这里找的话,其实传递进来的initialize其实是NO,不会进入初始化,但是咱们找到的地方是对的,我们继续来看代码,了解完实现之后,我们继续看哪里调用该函数时传递的initialize的值是YES。接下来我们进入_class_initialize函数,代码依旧很长,只截取了相关的部分代码。

    /***********************************************************************
    * class_initialize.  Send the '+initialize' message on demand to any
    * uninitialized class. Force initialization of superclasses first.
    **********************************************************************/
    void _class_initialize(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->superclass;
        if (supercls  &&  !supercls->isInitialized()) {
            _class_initialize(supercls);
        }
        
        // Try to atomically set CLS_INITIALIZING.
        {
            monitor_locker_t lock(classInitLock);
            if (!cls->isInitialized() && !cls->isInitializing()) {
                cls->setInitializing();
                reallyInitialize = YES;
            }
        }
        
        if (reallyInitialize) {
        	//	other code ..
    #if __OBJC2__
            @try
    #endif
            {
                callInitialize(cls);
    
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                                 pthread_self(), cls->nameForLogging());
                }
            }
    #if __OBJC2__
            @catch (...) {
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                                 "threw an exception",
                                 pthread_self(), cls->nameForLogging());
                }
                @throw;
            }
            @finally
    #endif
            {
                // Done initializing.
                lockAndFinishInitializing(cls, supercls);
            }
            return;
        }
        
        //	other code ..
    }
    

    在这里我们看到,首先如果父类存在并且父类没有初始化过,那么调用_class_initialize函数来初始化父类,直到整条集成链上的所有类都初始化完毕。之后调用callInitialize函数来初始化自己。

    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
        asm("");
    }
    

    这里很熟悉了,通过消息机制调用initialize方法。到这里能解释了,为什么initialize会调用父类的initialize方法。

    回来我们继续说一下什么时候调用lookUpImpOrForward是传递的initialize的值为YES

    1. _class_lookupMethodAndLoadCache3方法,最终调用是汇编层调用,也就是说我们的代码调用,代码调用前是需要这个类初始化完毕的。这里就是我们要找的入口
    2. objc_loadWeakRetainedweak_register_no_lock因为这两个方法会调用SEL_retainWeakReference这个selector,所以要判断是否已经初始化完毕。
    3. methodForSelectormethodForinstanceMethodFor这三个方法也是获取IMP的,所以也是需要这个类初始化完毕,否则通过Runtime增加的一些方法,可能无法获取到。

    总结

    本文主要通过官方文档、例子以及Runtime源码,分析了+initialize方法的调用,总结如下:

    1. 当代码执行到一个类第一次调用方法时,会调用这个类的+initialize方法
    2. 在调用自身类的+initialize方法之前,会判断其父类链上是否有类还没有执行+initialize方法,如果没有执行,那么执行。所以所有父类的+initialize方法都执行在前,子类的+initialize执行在后。
    3. 如果一个类有多个分类都实现了+initialize方法,那么会执行编译顺序的最后一个分类实现的+initialize方法
    4. 当一个类实现了+initialize方法,但是子类没有实现+initialize或者子类在实现+initialize方法中显式的调用的[super initialize]方法,那么该类的+initialize方法会调用多次,如果不想该方法被多次调用,可以在该类的+initialize方法通过if (self = [ClassName self])进行判断来避免多次调用。

    +load方法与+initialize方法的区别


    调用方式

    +load方法

    根据函数地址直接调用

    +initialize方法

    是通过objc_msgSend调用


    调用时机

    +load方法

    是Runtime加载类、分类的时候调用(如果不显式调用,只会调用一次)

    +initialize方法

    是类第一次接收到消息的时候调用(如果不显式调用,可能存在调用多次的风险)


    调用顺序

    +load方法
    • 先调用类的+load方法,再调用分类的+load方法
    • 有继承关系的类,先调用父类的+load,后调用子类的+load方法
    • 没有继承关系的类,会按照编译顺序来执行+load方法
    • 所有的分类,都按照编译顺序来执行+load方法
    +initialize方法
    • 先调用父类的+initialize方法,后调用子类的+initialize方法
    • 如果一个类有分类,那么会调用最后编译的分类实现的+initialize方法
    • 通过消息机制调用,当子类没有实现+initialize方法时,会调用父类的initialize方法
    展开全文
  • ThreadX最小移植写在开头平台介绍移植准备一个简单的裸机程序复制ThreadX源码到LED工程文件夹在LED工程中添加ThreadX文件修改tx_initialize_low_level.s文件**tx_initialize_low_level.s与startup_stm32l475xx.s的...

    写在开头

    本篇文章的知识大多源于安富莱_STM32-V6开发板ThreadX内核教程(V0.4).pdf,有兴趣的同学可以前往讨论学习。

    http://www.armbbs.cn/forum.php?mod=viewthread&tid=99514

    不同于硬汉哥的移植那样复杂,本篇文章仅进行了最简洁的ThreadX移植,类似于单片机最小系统,未作多余的开发。

    平台介绍

    硬件平台:STM32L475VET6(M4内核的芯片应该都适用);
    ThreadX版本:6.1.3;
    IDE:KEIL5 v5.31.0.0版本、STM32CubeMX;
    ARM编译器:AC5
    在这里插入图片描述

    移植

    准备一个简单的裸机程序

    使用STM32CubeMX生成一个MDK-ARM平台的LED工程。

    复制ThreadX源码到LED工程文件夹

    将ThreadX源码下的common文件夹,ports>cortex_m4>ac5文件夹拷贝到LED工程文件夹下。
    注:将ports>cortex_m4>ac5>example_build文件夹下的tx_initialize_low_level.s文件拷贝到ports>cortex_m4>ac5>src文件夹下,方便后期在工程中添加文件。详细步骤可参考安安富莱_STM32-V6开发板ThreadX内核教程(V0.4).pdf第4.4.1~4.4.3章节。

    在LED工程中添加ThreadX文件

    在LED工程中添加common和port文件、设置好头文件路径。

    在这里插入图片描述
    在这里插入图片描述

    修改tx_initialize_low_level.s文件

    修改思路:tx_initialize_low_level.s是ThreadX提供启动文件,其满足ThreadX需求却不满足mcu需求。而LED工程本身自带启动文件startup_stm32l475xx.s又不满足ThreadX的需求。所以我们需要将这两个文件结合起来使用,针对两个文件交集的部分,我们以LED工程自带的启动文件为准。

    tx_initialize_low_level.s与startup_stm32l475xx.s的简单分析

    ①两个启动文件都有对于堆栈指针的描述,我们删除左边,以右边为准
    在这里插入图片描述
    ②两个启动文件都有中断向量表,但tx_initialize_low_level.s只描述了部分中断向量,不够完整,所以我们删除左边,以右边为准在这里插入图片描述
    ③两个文件都有中断处理函数,我们删除左边,以右边为准
    在这里插入图片描述
    ④修改tx_initialize_low_level.s文件中的VOID _tx_initialize_low_level(VOID)代码段。将|Image$$ZI$$Limit|__tx_vectors替换为startup_stm32l475xx.s文件中的__initial_sp__Vectors
    在这里插入图片描述
    因为__initial_sp__Vectors声明在startup_stm32l475xx.s文件,所以应该在tx_initialize_low_level.s文件中引入上述两个变量。IMPORT |Image$$RO$$Limit| IMPORT |Image$$RW$$Base| IMPORT |Image$$ZI$$Base| IMPORT |Image$$ZI$$Limit| IMPORT __tx_PendSVHandler未使用,可以删除,修改好的代码如右图所示在这里插入图片描述

    ⑤两个文件都有初始化堆,我们删除左边,以右边为准
    在这里插入图片描述
    ⑥在tx_initialize_low_level.s文件定义SysTick_Handler代码段,只需添加两行代码,修改完成后如右图所示
    在这里插入图片描述
    ⑦修改tx_initialize_low_level.s文件中系统时钟,其与STM32CubeMX中设置的时钟一致,否则会出现奇怪的运行结果。这里我设置的是80M,如右图所示
    在这里插入图片描述
    修改完成后的tx_initialize_low_level.s文件如下所示:

    ;/**************************************************************************/
    ;/*                                                                        */
    ;/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
    ;/*                                                                        */
    ;/**************************************************************************/
    ;
    ;
    ;/**************************************************************************/
    ;/**************************************************************************/
    ;/**                                                                       */
    ;/** ThreadX Component                                                     */
    ;/**                                                                       */
    ;/**   Initialize                                                          */
    ;/**                                                                       */
    ;/**************************************************************************/
    ;/**************************************************************************/
    ;
    ;
        IMPORT  _tx_thread_system_stack_ptr
        IMPORT  _tx_initialize_unused_memory
        IMPORT  _tx_thread_context_save
        IMPORT  _tx_thread_context_restore
        IMPORT  _tx_timer_interrupt
        IMPORT  __main
        IMPORT  __initial_sp
        IMPORT  __Vectors
    ;
    ;
    SYSTEM_CLOCK        EQU     80000000
    SYSTICK_CYCLES      EQU     ((SYSTEM_CLOCK / 100) -1)
    
        AREA ||.text||, CODE, READONLY
    
    
    ;/**************************************************************************/
    ;/*                                                                        */
    ;/*  FUNCTION                                               RELEASE        */
    ;/*                                                                        */
    ;/*    _tx_initialize_low_level                          Cortex-M4/AC5     */
    ;/*                                                           6.1          */
    ;/*  AUTHOR                                                                */
    ;/*                                                                        */
    ;/*    William E. Lamie, Microsoft Corporation.                            */
    ;/*                                                                        */
    ;/*  DESCRIPTION                                                           */
    ;/*                                                                        */
    ;/*    This function is responsible for any low-level processor            */
    ;/*    initialization, including setting up interrupt vectors, setting     */
    ;/*    up a periodic timer interrupt source, saving the system stack       */
    ;/*    pointer for use in ISR processing later, and finding the first      */
    ;/*    available RAM memory address for tx_application_define.             */
    ;/*                                                                        */
    ;/*  INPUT                                                                 */
    ;/*                                                                        */
    ;/*    None                                                                */
    ;/*                                                                        */
    ;/*  OUTPUT                                                                */
    ;/*                                                                        */
    ;/*    None                                                                */
    ;/*                                                                        */
    ;/*  CALLS                                                                 */
    ;/*                                                                        */
    ;/*    None                                                                */
    ;/*                                                                        */
    ;/*  CALLED BY                                                             */
    ;/*                                                                        */
    ;/*    _tx_initialize_kernel_enter           ThreadX entry function        */
    ;/*                                                                        */
    ;/*  RELEASE HISTORY                                                       */
    ;/*                                                                        */
    ;/*    DATE              NAME                      DESCRIPTION             */
    ;/*                                                                        */
    ;/*  09-30-2020     William E. Lamie         Initial Version 6.1           */
    ;/*                                                                        */
    ;/**************************************************************************/
    ;VOID   _tx_initialize_low_level(VOID)
    ;{
        EXPORT  _tx_initialize_low_level
    _tx_initialize_low_level
    ;
    ;    /* Disable interrupts during ThreadX initialization.  */
    ;
        CPSID   i
    ;
    ;    /* Set base of available memory to end of non-initialised RAM area.  */
    ;
        LDR     r0, =_tx_initialize_unused_memory       ; Build address of unused memory pointer
        LDR     r1, =__initial_sp		                ; Build first free address
        ADD     r1, r1, #4                              ;
        STR     r1, [r0]                                ; Setup first unused memory pointer
    ;
    ;    /* Setup Vector Table Offset Register.  */
    ;
        MOV     r0, #0xE000E000                         ; Build address of NVIC registers
        LDR     r1, =__Vectors                       ; Pickup address of vector table
        STR     r1, [r0, #0xD08]                        ; Set vector table address
    ;
    ;    /* Enable the cycle count register.  */
    ;
    ;    LDR     r0, =0xE0001000                         ; Build address of DWT register
    ;    LDR     r1, [r0]                                ; Pickup the current value
    ;    ORR     r1, r1, #1                              ; Set the CYCCNTENA bit
    ;    STR     r1, [r0]                                ; Enable the cycle count register
    ;
    ;    /* Set system stack pointer from vector value.  */
    ;
        LDR     r0, =_tx_thread_system_stack_ptr        ; Build address of system stack pointer
        LDR     r1, =__Vectors                       ; Pickup address of vector table
        LDR     r1, [r1]                                ; Pickup reset stack pointer
        STR     r1, [r0]                                ; Save system stack pointer
    ;
    ;    /* Configure SysTick.  */
    ;
        MOV     r0, #0xE000E000                         ; Build address of NVIC registers
        LDR     r1, =SYSTICK_CYCLES
        STR     r1, [r0, #0x14]                         ; Setup SysTick Reload Value
        MOV     r1, #0x7                                ; Build SysTick Control Enable Value
        STR     r1, [r0, #0x10]                         ; Setup SysTick Control
    ;
    ;    /* Configure handler priorities.  */
    ;
        LDR     r1, =0x00000000                         ; Rsrv, UsgF, BusF, MemM
        STR     r1, [r0, #0xD18]                        ; Setup System Handlers 4-7 Priority Registers
    
        LDR     r1, =0xFF000000                         ; SVCl, Rsrv, Rsrv, Rsrv
        STR     r1, [r0, #0xD1C]                        ; Setup System Handlers 8-11 Priority Registers
                                                        ; Note: SVC must be lowest priority, which is 0xFF
    
        LDR     r1, =0x40FF0000                         ; SysT, PnSV, Rsrv, DbgM
        STR     r1, [r0, #0xD20]                        ; Setup System Handlers 12-15 Priority Registers
                                                        ; Note: PnSV must be lowest priority, which is 0xFF
    ;
    ;    /* Return to caller.  */
    ;
        BX      lr
    ;}
    ;
    ;
    
    ;/* Define shells for each of the unused vectors.  */
    ;
        EXPORT  __tx_BadHandler
    __tx_BadHandler
        B       __tx_BadHandler
    
        EXPORT  __tx_SVCallHandler
    __tx_SVCallHandler
        B       __tx_SVCallHandler
    
        EXPORT  __tx_IntHandler
    __tx_IntHandler
    ; VOID InterruptHandler (VOID)
    ; {
        PUSH    {r0, lr}
    
    ;    /* Do interrupt handler work here */
    ;    /* .... */
    
        POP     {r0, lr}
        BX      LR
    ; }
    
            EXPORT  __tx_SysTickHandler
    		EXPORT  SysTick_Handler
    __tx_SysTickHandler
    SysTick_Handler
    ; VOID TimerInterruptHandler (VOID)
    ; {
    ;
        PUSH    {r0, lr}
        BL      _tx_timer_interrupt
        POP     {r0, lr}
        BX      LR
    ; }
    
        EXPORT  __tx_NMIHandler
    __tx_NMIHandler
        B       __tx_NMIHandler
    
        EXPORT  __tx_DBGHandler
    __tx_DBGHandler
        B       __tx_DBGHandler
    
        ALIGN
        LTORG
        END
    
    

    注释LED工程原本的void PendSV_Handler(void);void SysTick_Handler(void);

    在这里插入图片描述
    在这里插入图片描述

    提供一个简单的测试任务

    uint8_t my_buff[1024];
    uint8_t you_buff[1024];
    
    TX_THREAD my_thread,you_thread;
    void my_thread_entry(ULONG thread_input)
    {
    	/* Enter into a forever loop. */
    	while(1)
    	{
    		tx_thread_sleep(3);
    	}
    }
    
    void you_thread_entry(ULONG thread_input)
    {
    	while(1)
    	{
    		tx_thread_sleep(3);
    	}
    }
    
    void tx_application_define(void *first_unused_memory)
    {
    	/* Create my_thread! */
    	tx_thread_create(&my_thread, "My Thread",
    	my_thread_entry, 0x1234, my_buff, 1024,
    	3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
    
    	tx_thread_create(&you_thread, "You Thread",
    	you_thread_entry, 0x1234, you_buff, 1024,
    	3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
    }
    

    在main函数中添加头文件#include "tx_api.h"后,调用tx_kernel_enter();即可运行任务。

    记录一下踩过的坑

    ①STM32CubeMX生成的工程默认不勾选Reset and Run,程序下载后不运行,建议勾选。
    ②tx_initialize_low_level.s文件中的系统时钟最好与STM32CubeMX中保持移植,否则可能出现未知的运行结果。
    ③开发时建议将KEIL的代码优化设置为0

    展开全文
  • 如果子类实现了 initialize,那么就直接执行子类的 initialize 理论上只会调用一次,但是因为采用了 objc_msgSend 来调用,所以如果子类没有实现 initialize,那么就会多次调用父类的 initialize,可以通过添加 if ...

    一、+load

    直接过结论并且验证:

      1. 在main 函数执行之前调用,具体调用是在 dyld 将类加载进入 runtime 时,在 dyld_start 之后由 dyld 调用;

    调用栈如图:


    load方法通过dyld调用
      1. 分类和类分别处理,存储在两个全局表中,所有类的 load 方法调用完毕之后再调用分类的 load 方法;

    源码如下:

    // List of classes that need +load called (pending superclass +load)
    // This list always has superclasses first because of the way it is constructed
    static struct loadable_class *loadable_classes NOBSS = NULL;
    
    // List of categories that need +load called (pending parent class +load)
    static struct loadable_category *loadable_categories NOBSS = NULL;
    
    __private_extern__ void prepare_load_methods(header_info *hi)
    {
        size_t count, i;
    
        rwlock_assert_writing(&runtimeLock);
    
        class_t **classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            class_t *cls = remapClass(classlist[i]);
            // 内部会调用add_class_to_loadable_list添加到loadable_classes数组中
            schedule_class_load(cls);
        }
    
        category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = categorylist[i];
            // Do NOT use cat->cls! It may have been remapped.
            class_t *cls = remapClass(cat->cls);
            realizeClass(cls);
            assert(isRealized(cls->isa));
           // 这个方法中会存入 loadable_categories
            add_category_to_loadable_list((Category)cat);
        }
    }

    将类添加到数组的代码如下,分类的就不列出了:

    static void schedule_class_load(class_t *cls)
    {
        assert(isRealized(cls));  // _read_images should realize
    
        if (cls->data->flags & RW_LOADED) return;
    
        class_t *supercls = getSuperclass(cls);
        if (supercls) schedule_class_load(supercls);
    
        add_class_to_loadable_list((Class)cls);
        changeInfo(cls, RW_LOADED, 0); 
    }

    dyld 会分别将类和分类添加到两个全局数组中,以后循环取出这两个数组中的元素,递归执行 load。这两个数组也就是分类 load 不覆盖原来类的 load 方法的本质;

      1. 在类先于分类调用的大前提下,分类中 load 方法的调用顺序取决于符号表中的顺序,也就是编译顺序;

    如下:


    load方法和macho
      1. 递归调用类的 +load 方法,因此父类会先于子类调用,分类最后调用;
      1. dyld 中并没有使用 objc_msgSend 来调用函数,而是直接执行,所以不存在方法查找和消息传递机制,所以子类未实现 load 方法的情况下也不去调用父类的 load;

    源码如下:

    __private_extern__ void call_load_methods(void)
    {
        static BOOL loading = NO;
        BOOL more_categories;
    
        recursive_mutex_assert_locked(&loadMethodLock);
    
        // Re-entrant calls do nothing; the outermost call will finish the job.
        if (loading) return;
        loading = YES;
    
        do {
            // 1. Repeatedly call class +loads until there aren't any more
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2. Call category +loads ONCE
            more_categories = call_category_loads();
    
            // 3. Run more +loads if there are classes OR more untried categories
        } while (loadable_classes_used > 0  ||  more_categories);
    
        loading = NO;
    }

    这里也可以看出,两个 while 循环,先通过 call_class_loads 执行所有类的 load 方法,再通过 call_category_loads 执行分类的 load 方法。

    其中,执行类的 load 方法代买如下:

    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 = NULL;
        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;
            IMP load_method = classes[i].method;
            if (!cls) continue; 
    
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
            }
            (*load_method) ((id) cls, SEL_load);
        }
        
        // Destroy the detached list.
        if (classes) _free_internal(classes);
    }

    从上面可以看到,直接取出类地址之后执行,并没有调用 objc_msgSend。因此,如果子类没有实现 load 方法,并不会去调用父类的 load;

    二、+initalize

    先看一眼源码:

    __private_extern__ void _class_initialize(Class cls)
    {
        Class supercls;
        BOOL reallyInitialize = NO;
    
        // Get the real class from the metaclass. The superclass chain 
        // hangs off the real class only.
        cls = _class_getNonMetaClass(cls);
    
        // Make sure super is done initializing BEFORE beginning to initialize cls.
        // See note about deadlock above.
        supercls = _class_getSuperclass(cls);
        if (supercls  &&  !_class_isInitialized(supercls)) {
            _class_initialize(supercls);
        }
        
        // Try to atomically set CLS_INITIALIZING.
        monitor_enter(&classInitLock);
        if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
            _class_setInitializing(cls);
            reallyInitialize = YES;
        }
        monitor_exit(&classInitLock);
        
        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);
            
            // 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: calling +[%s initialize]",
                             _class_getName(cls));
            }
    
            ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: finished +[%s initialize]",
                             _class_getName(cls));
            }        
            
            // Done initializing. 
            ......
    }

    代码中有几个关键点:

      1. 初始化自己之前,递归执行父类的初始化操作;
    supercls = _class_getSuperclass(cls);
        if (supercls  &&  !_class_isInitialized(supercls)) {
            _class_initialize(supercls);
        }

    这里很明显是先判断父类有没有初始化完成,如果没有则递归执行父类的初始化;

      1. 初始化方法是通过 objc_msgSend 调用的,需要经过方法查找和消息转发的过程;
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

    因此,可以做出几个结论:

    1. initialize 方法是在这个类第一次被使用到时才调用,具体为第一次调用该类的相关方法;
    2. 父类的 initialize 先执行;
    3. 如果子类没有实现 initialize,则会调用父类的initialize;
    4. 如果子类实现了 initialize,那么就直接执行子类的 initialize
    5. 理论上只会调用一次,但是因为采用了 objc_msgSend 来调用,所以如果子类没有实现 initialize,那么就会多次调用父类的 initialize,可以通过添加 if (self == [ClassName self]) 来进行判断;
    6. 不像 load 方法,会区分类和分类保存在两个数组中分别执行, 分类的 initialize 方法会覆盖原来类的 initialize,且遵循分类的编译顺序原则,最靠后的分类最终替换掉之前的 initialize 方法;

    总结: load 和 initialize 方法本质都是做初始化的,只不过级别或者说针对的过程不一样。load 只会调用一次,在 main 方法调用之前做初始化,比如方法交换。initialize 方法针对的是 main 方法之后,而且是懒加载,使用到时才初始化。鉴于 objc_msgSend 的机制,存在多次调用的可能,但是可以使用代码进行判断。

    展开全文
  • initialize和init区别

    2019-09-22 11:13:51
    2008-03-23 20:21:08.820 initialize_example[30513:10b] Duck initialize class:Chicken 2008-03-23 20:21:08.821 initialize_example[30513:10b] Duck init 如果你希望确定只用了initialize一次用来实现某些...
  • 情况2:父类实现initialize,init,子类未实现initialize(注释掉子类的方法),实现init,父类的分类未实现initialize,init情况,打印结果: 2018-10-04 15:02:50.767970+0800 InterviewDemo[4360:682382] +...
  • Lazy.Object.Initialize

    2016-09-01 09:11:05
    延迟加载类Lazy的使用示例。
  • 在lib/person.rb中定义一个Person类, lib/person.rb提供一个#initialize方法,该方法接受一个#initialize的参数。 该参数应存储在@name实例变量中。 2. Dog#initialize使用名称和品种默认为“ Mutt” Dog#...
  • error.GitError: cannot initialize work tree

    千次阅读 2021-01-13 15:08:05
    error.GitError: cannot initialize work tree 1.Error Fetching projects: 100% (725/725), done. Checking out files: 100% (4605/4605), done.out files: 41% (1902/4605) Checking out files: 100% (4371/4371...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 555,697
精华内容 222,278
关键字:

initialize