const/static/extern的使用

const 与宏的区别

  • 编译时刻:宏是预编译(编译之前处理),const是编译阶段。
  • 编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。
  • 宏的好处:宏能定义一些函数,方法。 const不能。
  • 宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。

注意:使用宏,并不会生成很多内存,宏定义的是常量,常量都放在常量区,只会生成一份内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 常见的常量,使用宏替换
#define GGAccount @"accunt"
// 字符串常量
static NSString * const account = @"accunt";

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSString *acc = GGAccount;
NSLog(@"acc: %p---%p---%@", acc, &acc, acc.class);
NSLog(@"account:%p---%p---%@", account, &account, account.class);
}

// 打印结果
acc: 0x1084f9078---0x7ffee7705fe8---__NSCFConstantString
account:0x1084f9078---0x1084f9070---__NSCFConstantString
// 变量 acc 和常量 account 指向的是同一块内存区域,即字符串常量 @"accunt" 所在的内存地址
// account 被 const 修饰,所以是常量,内存在常量区

const 作用:限制类型

  • const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p)
  • const修饰的变量是只读的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int a = 10; 		// 定义变量
a = 100; // 允许修改值

// b的值不允许修改,这两种写法作用相同
const int b = 20;
int const b = 20;
// b = 10; // 不允许修改

// const:修饰指针变量*p,带*的变量,就是指针变量.
// 定义一个 const 修饰的指针变量,const修饰的是 *p,即 p 访问的内存空间
const int *p = &a;
int c = 300;
p = &c; // 允许修改 p 的值,即所指向的地址
// *p = 20; // 不允许修改 p 访问的内存空间的值

// const:修饰指针变量 p1
int * const p1 = &a; // const修饰的是 p,即 p 自身的内存空间
// p1 = &c; // 不允许修改 p 的值,即所指向的地址
*p1 = 30; // 可以修改 p 访问的内存空间的值

static 作用

  • 修饰局部变量:
    1. 延长局部变量的生命周期,程序结束才会销毁。
    2. 局部变量只会生成一份内存,只会初始化一次。
    3. 改变局部变量的作用域。
  • 修饰全局变量
    1. 只能在本文件中访问,修改全局变量的作用域,生命周期不会改
    2. 避免多个同名全局变量重复定义引发冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// static 修饰全局变量,只能在本文件中访问,生命周期不会改
static int age = 20;

// static 修饰局部变量
- (void)test
{
for (NSInteger i = 0; i < 10; i++) {
static int age = 0;
age++;
NSLog(@"%d----%p",age, &age);
}
}

// 打印结果,可以看出 age 只初始化了一次,存在一份内存,每次循环使用的都是同一个变量
1----0x1018e3f80
2----0x1018e3f80
3----0x1018e3f80
4----0x1018e3f80
5----0x1018e3f80
6----0x1018e3f80
7----0x1018e3f80
8----0x1018e3f80
9----0x1018e3f80
10----0x1018e3f80

extern 作用

  • 只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量

  • 先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找。

1
2
3
4
5
6
7
8
9
10
// 在一个 .m 文件定义一个全局变量
int p = 1000;

// 在另一个文件中,使用 extern 可以访问到这个变量
- (void)viewDidLoad {
[super viewDidLoad];

extern int p;
NSLog(@"%d",p);
}

static 与 const 组合使用

  • 作用:staticconst组合可声明一个只读的静态变量

  • 使用场景:在一个文件中经常使用的字符串常量,定义成静态全局只读变量,可以使用staticconst组合。如:Cell 的 ReuseIdentifier

1
2
3
4
5
6
7
8
9
10
11
// 开发中常用 static 修饰全局变量,只改变作用域,防止重复声明全局变量
// 开发中声明的全局变量,有些不希望外界改动,只允许读取。
// 比如一个基本数据类型不希望别人改动
// 声明一个静态的全局只读常量
static const int a = 20;

// 开发中经常拿到 key 修改值,因此用 const 修饰 key,表示 key 只读,不允许修改。
static NSString * const key = @"name";

// cell 的重用字符串,每次获取 cell 时都会使用,创建一个静态常量,避免反复创建
static NSString * const cellIdentifier = @"cellIdentifier";

extern 与 const 组合使用

  • 作用:externconst组合,只需要定义一份全局变量,多个文件共享。

  • 使用场景:在多个文件中经常使用的同一个字符串常量,可以使用externconst组合

  • 全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个GlobeConst文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找。

1
2
3
4
5
// .h
extern NSString * const nameKey;

// .m
NSString * const nameKey = @"name";