OC第二天-实例变量可见度,setter和getter方法,导入头文件

本文深入探讨了面向对象编程中的实例变量可见度、方法、setter、getter的应用,包括属性的公开、受保护和私有访问特性,以及如何通过自定义初始化方法和访问器方法实现属性的设置和获取。

实例变量可见度、方法、setter、getter
Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject{

//    在声明成员变量的时候,如果没有使用关键字进行说明,默认是受保护类型
    NSString *_address;



    @public//公开的  可以在本类和类外面访问和修改;可以在任何地方访问和修改;
    NSString *_name;

    @protected//受保护的  作用域 在本类和子类 ;
    NSString *_sex;

    @private//私有的  只能在本类里面对他修饰的成员进行访问 和 修改
    int      _age;


    @package//作用于整个框架Cocoa框架

}

//setter(设置器)的书写格式:实例前面的_去掉,将首字母大写,然后前面加上set;设置器方法没有返回值
//即set+首字母⼤写的实例变量名(忽略下划线)
-(void) setAge:(int)age;
-(void) setName:(NSString *)name;
-(void) setAddress:(NSString *)address;
-(void) setSex:(NSString *)sex;

//getter(访问器)书写格式:变量名字去掉下划线 加上返回值类型

-(int)age;
-(NSString *)name;
-(NSString *)sex;
-(NSString *)address;

//声明一个 自定义的初始化方法
-(id)initWithName:(NSString *)name;


//方法标识后面的括号中的类型是返回值类型;void表示空类型,没有返回值;
-(void) print;

//获取年龄
-(int) getAge;

//id 和instancetype比较: 1、都表示对象类型;2、id在编译阶段不确定类型,instancetype在编译阶段已经确定类型 3、id可以当做参数类型来使用,而instancetype不可以,只能当做返回值类型
-(instancetype) printf;




//:冒号表示方法有参数;:冒号后面就是这个方法的参数 ()里面是参数的类型,()后面是参数的名字;冒号前面是参数的修饰词,就是对参数的解释
//方法名字:initWithName:age:::
-(id) initWithName:(NSString *)name age:(int)age ;


//声明一个设置方法
-(void) setName:(NSString *)name
            age:(int)age
        address:(NSString *)address;

@end

//学生类的声明部分
@interface Student : Person

@end

Person.m

#import "Person.h"

@implementation Person

//设置器的实现
-(void) setAge:(int)age{
    _age = age;
}
-(void) setName:(NSString *)name{
    _name = name;
}
-(void) setAddress:(NSString *)address{
    _address = address;
}
-(void) setSex:(NSString *)sex{
    _sex = sex;
}

//访问器(获取器)的实现
-(int)age{
    return _age;
}
-(NSString *)name{
    return _name;
}
-(NSString *)sex{
    return _sex;
}
-(NSString *)address{
    return _address;
}


//实现一个 自定义的初始化方法
-(id)initWithName:(NSString *)name{
//    先让父类执行一次初始化方法
    self = [super init];
//    然后判断,并对实例变量进行设置
    if (self) {
        _name = name;
    }
    return self;
}


-(void) print{
    NSLog(@"_age = %d",_age);
}

-(int) getAge{
    return 20;
}


-(void) setName:(NSString *)name
            age:(int)age
        address:(NSString *)address{
    _name = name;
    _age = age;
    _address = address;
}

@end

@implementation Student

-(void)print{
    Person *p = [[Person alloc] init];
    p -> _sex = @"man";

}

@end

AudiCar.h

#import <Foundation/Foundation.h>

@interface AudiCar : NSObject{
    @public
    NSString *_color;
    NSString *_brand;
    float    _price;
}
//setter设置器
-(void)setColor:(NSString *)color
          Brand:(NSString *)brand
          Price:(float)price;
//声明两个方法
-(void) print;
//
+(void) run;

@end

AudiCar.m

#import "AudiCar.h"

@implementation AudiCar

-(void)setColor:(NSString *)color
          Brand:(NSString *)brand
          Price:(float)price{
    _color = color;
    _brand = brand;
    _price = price;
}

-(void) print{
    NSLog(@"sf");
}

+(void) run{
    self;//表示类本身,就是类的名字

    //    不能在+方法中访问实例变量
    //    _address;
}

@end

Man.h

#import <Foundation/Foundation.h>
//#import "Woman.h"
@class Woman;//用class关键字声明类,解决用import导入文件时产生的循环编译的问题class只是一个声明的作用,就是告诉编译器 这个类是存在的,但是不编译;

@interface Man : NSObject{
    Man *_woman;

}
@end

Man.m

#import "Man.h"

@implementation Man

@end

Woman.h

#import <Foundation/Foundation.h>
//#import "Man.h"
@class Man;

@interface Woman : NSObject{

    Man *_MAN;
}
@end

Woman.m

#import "Woman.h"

@implementation Woman

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"
#import "AudiCar.h"

#import "Man.h"
#import "Woman.h"


//#include "Person.h"//是C语言中导入头文件的方法,如果在一个文件中重复导入相同的文件,就会出错
//#include "Person.h"

#import "Person.h"//解决了重复导入头文件的问题,避免重复编译的错误;
#import "Person.h"
#import "Person.h"


int main(int argc, const char * argv[]) {

    Person *person = [[Person alloc]init];

    [person setName:@"zhangdan" age:20 address:@"北京"];


    AudiCar *p = [[AudiCar alloc] init];
    [p setColor:@"red" Brand:@"audi" Price:1111111];
    [p print];


    Man *man = [[Man alloc] init];
    Woman *woman =[[Woman alloc] init];


    return 0;
}
<think>好的,用户现在想了解在STM32开发中为什么尽量避免使用全局变量以及替代方案。我需要结合之前的回答用户提供的引用内容来组织答案。首先,用户之前的提问是关于不同变量类型的区别,现在进一步深入探讨全局变量的弊端替代方法。我需要先回忆之前的回答,特别是关于全局变量的存储位置、作用域潜在问题。同时,用户提供的引用[1]提到STM32的存储地址分配,引用[2]建议使用节省空间的数据类型,这可能与替代方案相关。 接下来,我需要整理避免使用全局变量的原因。可能包括:内存占用(静态存储区)、数据耦合、并发访问问题、可维护性差等。每个原因需要结合STM32的特性,比如有限的SRAM资源,中断RTOS中的竞争条件。然后考虑替代方案。可能包括使用局部变量、静态局部变量(但需谨慎)、结构体封装、函数参数传递、模块化设计、使用硬件外设寄存器等。引用[2]提到使用小数据类型,这可以作为优化全局变量的一种方法,但用户的问题更倾向于避免使用,所以可能需要调整这个点,比如在必须使用时优化空间。 需要注意用户可能是在嵌入式开发中遇到内存管理或代码结构的问题,需要强调嵌入式系统的资源限制实时性要求。替代方案需要具体,比如模块化设计中的封装,使用getter/setter函数,或者利用外设寄存器直接操作,减少变量使用。 同时,用户之前的回答提到了栈溢出风险,所以在替代方案中推荐使用局部变量时,要提醒注意栈大小,避免溢出。此外,静态局部变量虽然延长生命周期,但仍在静态存储区,可能不如完全避免全局变量好。最后,生成相关问题需要考虑用户可能进一步的问题,比如如何检测栈溢出、模块化设计的具体方法、RTOS中的变量管理等。需要确保问题与当前主题相关,并引导用户深入探讨。</think>### STM32开发中避免使用全局变量的原因及替代方案 #### 一、避免全局变量的主要原因 1. **内存资源占用** STM32的SRAM有限(通常4-512KB),全局变量存储在静态存储区(地址范围$0x20000000$开始的区域)[^1],会永久占用内存。例如: ```c uint8_t global_buffer[1024]; // 永久占用1KB SRAM ``` 2. **数据耦合风险** 全局变量可被任意函数修改,导致模块间耦合度升高。在中断服务函数与主程序共享全局变量时,可能引发**竞态条件**(Race Condition)[^2]。 3. **可维护性下降** 跨文件访问全局变量需要`extern`声明(如`extern int g_value;`),当项目规模扩大时,追踪变量修改路径变得困难。 4. **启动时间延长** C库在启动阶段会对全局变量进行**零初始化**(Zero-initialization),当存在大量全局变量时,会显著增加启动时间(如从Flash启动时)。 #### 二、推荐替代方案 | 方案类型 | 实现方式 | 优势 | |-----------------|--------------------------------------------------------------------------|----------------------------------------------------------------------| | **局部变量** | 在函数内部定义临时变量 | 自动释放内存,避免长期占用(需注意栈空间限制) | | **静态局部变量**| 使用`static`限定词(如`static int count=0;`) | 限制作用域,保持数据持久性 | | **结构体封装** | 通过结构体聚合相关数据(如`typedef struct {int x,y;} Point;`) | 提升数据组织性,降低全局变量数量 | | **函数参数传递**| 使用指针或返回值传递数据(如`void process(int *input)`) | 明确数据流向,增强代码可读性 | | **模块化设计** | 将数据与操作封装在.c文件中,通过头文件暴露接口 | 实现信息隐藏,降低耦合度 | | **寄存器直访** | 直接操作外设寄存器(如`TIM2->CR1 |= TIM_CR1_CEN;`) | 避免中间变量,节省内存(外设寄存器地址在$0x40000000$区域[^1]) | #### 三、关键代码示例 ```c // 模块化封装示例(module.c) static int private_data; // 模块私有静态变量 void module_set(int value) { private_data = value; } int module_get(void) { return private_data; } // 中断安全的数据传递 void DMA_IRQHandler(void) { static __IO uint32_t buffer[64]; // 使用volatile确保可见性 // 通过DMA直接访问buffer(地址$0x2000xxxx$) } ``` #### 四、性能优化建议 1. **栈空间监控** 修改启动文件中的`Stack_Size`定义(默认值$0x400$即1KB),根据最大函数调用深度调整: ```armasm Stack_Size EQU 0x800 ; 改为2KB ``` 2. **数据类型优化** 使用`uint8_t`代替`int`可节省75%内存($1$字节 vs $4$字节)[^2],例如: ```c uint8_t sensor_value = 0; // 替代int类型 ``` 3. **内存布局分析** 通过.map文件查看变量地址分布(全局变量在$0x20000000$+区域,栈在SRAM末端)。 #### 五、特殊场景下的全局变量使用 1. **中断共享数据** 必须使用`volatile`限定词并配合关中断保护: ```c volatile uint32_t g_irq_flag = 0; void EXTI0_IRQHandler() { __disable_irq(); g_irq_flag = 1; __enable_irq(); } ``` 2. **RTOS任务通信** 优先使用消息队列、信号量等RTOS原生机制,而非全局变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值