_weak 弱函数目录

  • 一、修饰函数
  • 1、回调函数(钩子函数)
  • 二、修饰变量(了解)
  • 一、修饰函数

    两个重要点:
    1、__weak__attribute__((weak)) 在声明和定义的时候,其所处的位置有不同。
    2、__weak 仅在函数定义中使用时才会生成弱函数。而在任何情况下(声明和定义) __attribute__((weak)) 都会生成弱函数,无论是用于函数定义还是用于函数声明中!

            用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。

            表示弱声明,若外部文件没有声明EXTI0_IRQHandler函数,则在编译链接的阶段,链接本汇编起始startup_stm32f40_41xxx.s文件即启动代码中的EXTI0_IRQHandler函数。反之,链接外部文件中的EXTI0_IRQHandler函数。

    __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
      /* Prevent unused argument(s) compilation warning */
      UNUSED(GPIO_Pin);
    
      /* NOTE: This function should not be modified, when the callback is needed,
                the HAL_GPIO_EXTI_Callback could be implemented in the user file
       */ 
    }
    

    从这个函数中的语句和注释来看,这个函数其实什么都没有做。
    我们需要在用户文件中,自己再定义一个一模一样的函数,只是我们自己定义的函数,不需要指明是弱函数。

    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
      if (GPIO_Pin == GPIO_PIN_13)
      {
        /* Toggle LED2 */
        HAL_GPIO_TogglePin( GPIOA,  GPIO_PIN_5);
       
      }
    }
    

            程序在编译的时候,如果发现有两个相同名称的函数,而且其中一个是弱函数,就会忽略弱函数,使用正常的函数进行编译;如果发现只有一个弱函数,那还是会使用弱函数参与编译。

    /* test1.c */
    #include <stdio.h>
    
    void HelloFun(void)
    {
        printf("my hello.");
    }
    
    /* test2.c */
    #include <stdio.h>
    
    #define __weak __attribute__((weak))
    
    __weak void HelloFun(void)
    {
        printf("default hello.");
    }
    
    void main(void)
    {
        HelloFun();
    }
    

            ​__weak void HelloFun(void)函数和void HelloFun(void)函数不能在同一个.c文件中(当然,如果在同一个文件中,就没有了什么意义。因为弱函数一般用于几个模块之间的交互接口,哪有把几个模块写一个文件中的)。上面的程序中,如果我们在test1.c中没有定义HelloFun()函数,则编译器会使用test2.c中的HelloFun()函数。因此程序会打印default hello.如果在test1.c中定义了HelloFun()函数。则打印my hello.

            c99并没有__weak关键字。此关键字是编译器外扩的。所以不同的编译器可能不一样。比如gcc编译链中并没有这个关键字。而是使用__attribute__((weak))代替。为了方便移植,我们可以宏定义,如下:

    #ifndef __weak
    #define __weak __attribute__((weak))
    #endif
    

            原理:连接器发现同时存在弱符号和强符号,有限选择强符号,如果发现不存在强符号,只存在弱符号,则选择弱符号。如果都不存在:静态链接,编译时会报错,动态链接:系统无法启动。

            综上所述:如果我们没有在工程中其他地方重新定义 HAL_GPIO_EXTI_Callback()函数,那么 HAL_Init 初始化函数执行的时候, 会默认执行 stm32f4xx_hal.c 文件中定义的 HAL_GPIO_EXTI_Callback函数,而这个函数没有任何控制逻辑。
    如果用户在工程中重新定义函数 HAL_GPIO_EXTI_Callback,那么调用 HAL_Init 之后,会执行用户自己定义的 HAL_GPIO_EXTI_Callback函数而不会执行 stm32f4xx_hal.c 默认定义的函数。也就是说,表面上我们看到函数 HAL_GPIO_EXTI_Callback被定义了两次,但是因为有一次定义是弱函数,使用了__weak修饰符,所以编译器不会报错。

    weak属性只会在静态库(.o .a )中生效,动态库(.so)中不会生效。

    从上面的外部中断函数看出,_weak弱函数声明经常会出现在回调函数当中。

    1、回调函数(钩子函数)

    概念:函数实现方,不方便直接调用该函数, 而是有函数接口提供方简介调用该函数,称为回调函数
    示例: 系统中的信号处理函数,就是一个比较典型的回调函数
    1

    二、修饰变量(了解)

    不持有对象,所以在超出其变量作用域时,对象即被释放
    使用方式

    __weak int i
    __attribute__((weak)) 可以声明弱变量,并且其声明方式与 __weak 相比更加灵活。
    extern int Variable_Attributes_weak_1 __attribute__((weak));

            修饰变量在代码编写用的比较少。
            循环引用容易发生内存泄漏。所谓的内存泄漏就是应当废弃的对象在其作用域之外继续存在。
    此代码的本意是赋予变量 test0 的对象 A 和赋予变量 test1 的对象 B 在超出其变量作用域时被释放,即在对象不被任何变量持有的状态下予以废弃。但是,循环引用使得对象不能被再次废弃。
            以下情况,虽然只有一个对象,也会出现循环引用:

    id test = [[Test alloc] init];

    [test setObject:test];

            因为带 __weak 修饰符的变量(即弱引用)不持有对象,所以在超出其变量作用域时,对象即被释放。如果像以下内容将可能发生循环引用的类成员变量改成附有 __weak 修饰符的成员变量的话,该现象是可以避免的。
    原文:_weak修饰符详解

    @inerface Test : NSObject

    {

    id __weak obj_;
    

    }

  • (void)setObject:(id __strong)obj;
  • @end

    物联沃分享整理
    物联沃-IOTWORD物联网 » 理解和使用_weak弱函数

    发表评论