_weak 弱函数
综上所述:如果我们没有在工程中其他地方重新定义 HAL_GPIO_EXTI_Callback()函数,那么 HAL_Init 初始化函数执行的时候, 会默认执行 stm32f4xx_hal.c 文件中定义的 HAL_GPIO_EXTI_Callback函数,而这个函数没有任何控制逻辑。用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数
_weak 弱函数目录
一、修饰函数
两个重要点:
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、回调函数(钩子函数)
概念:函数实现方,不方便直接调用该函数, 而是有函数接口提供方简介调用该函数,称为回调函数
示例: 系统中的信号处理函数,就是一个比较典型的回调函数
二、修饰变量(了解)
不持有对象,所以在超出其变量作用域
时,对象即被释放
使用方式
:
__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
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)