目录
- 前言
- extern关键字
- static关键字
- const关键字
- 联合使用
- static和extern
- static和const
- extern和const
- auto关键字
先了解一下静态变量所在的全局/静态区的特点:【iOS】内存五大分区
前言
上面提到的全局/静态区中存放的是全局变量或静态变量:
全局变量是指变量值可以在运行时被动态修改
静态变量是static
修饰的变量,包含静态局部变量和静态全局变量
extern关键字
extern
只能用来修饰全局变量,不能修饰其他变量,一般定义的全局变量默认带有extern
,只不过被省略了,多用于跨文件引用
1. 与.h文件的关系
.h
文件时为了更好地方便其他文件去调用本文件中的变量、属性和方法,作用同extern
如果在.h
文件中声明全局变量,在OC中是不合法的,主要在@interface ExternModel : NSObject
和@end
之间,当然如果在他们两个之外也是可以声明的:
应显式声明extern
表明其不是成员变量,而是全局变量:
@interface ExternModel : NSObject
extern NSString* xyString;
@end
常见用法:
.h
结合extern
使用:如果在.h
文件中声明了extern
全局变量,那么在同一个类中的.m
文件对全局变量的赋值必须时:数据类型+变量名(与声明时一致) = XXXXX
,并且在调用时导入.h
文件:
// ExternModel.h
#import <Foundation/Foundation.h>
@interface ExternModel : NSObject
extern NSString* xyString;
@end
// ExternModel.m
#import "ExternModel.h"
@implementation ExternModel
NSString* xyString = @"Hello!!";
@end
例如,在viewController.m
或main.m
中调用,则可以引入ExternModel.h
,否则无法识别全局变量
当然也可以通过不导入头文件的方式进行调用,通过extern调用,来获取全局变量的值
2. extern引用变量
如果在其他文件中访问一个类的全局变量,可以不用导入.h
文件,通过extern
去直接访问:
#import <Foundation/Foundation.h>
//#import "ExternModel.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量
extern NSString* xyString;
NSLog(@"%@", xyString);
}
return 0;
}
// Hello!!
static关键字
static
修饰变量后该变量就被定义成为一个静态变量
静态全局变量
static
修饰全局变量,存储区仍在全局/静态区,但限定了该全局变量只能在当前源文件访问(从定义之处开始到文件结尾),限制了其作用域,在外部文件中是不可见的
好处: 定义后只会指向固定的指针地址,供当前文件使用,同一源程序的其他文件中可以使用相同名字的变量,不会发生冲突
不足: 存在的生命周期长,从定义直到程序结束
建议: 从内存优化和程序编译的角度来说,尽量少用全局静态变量,因为存在的生命周期长,一直占用空间。程序运行时会单独加载一次全局静态变量,过多的全局静态变量会造成程序启动慢
静态局部变量
static
修饰局部变量,存储区由栈区变为静态区(.rwdata),延长了该局部变量的生命周期,虽然直到源程序结束该变量才会被销毁,但其作用域仍为局部,即当定义该变量的函数或者语句块结束时,就不可使用,尽管该变量仍存在
好处: 定义后只会存在一份值,只会初始化一次,每次调用都是使用的同一个对象内存地址的值,并没有重新创建,节省空间
不足: 存在的生命周期长,从定义直到程序结束
建议: 静态全局和局部变量从根本上没有什么区别,只是作用域不同而已。如果仅一个类中的对象和类方法使用并且值可变,可以定义全局静态变量
静态函数
在函数的返回类型前加上关键字static
,函数就被定义成为静态函数
函数的实现和声明默认情况下是extern
的,当函数被static
修饰成为静态函数后,该函数的作用域仅限于定义它的源文件,同一程序的其他文件无法直接调用该函数,也无法通过外部链接访问
好处: 同一源程序的其他文件中可以使用相同名字的变函数,不会发生冲突;编译器可对static
静态函数进行更多的优化,因为已知该函数不会在其他源文件中使用
const关键字
const
是用来修饰常量(只读值)的,注意与宏的区别(EOC 2.0里面有提到《Effective Objective-C 2.0》读书笔记——熟悉Objective-C,良好的编码习惯提倡我们多用类型常量,少用#define
类型常量):
宏的好处:宏能定义一些函数、方法;const不能
宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换;宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误
const
仅仅用来修饰右边的变量:
// 定义变量
int a = 1;
// 允许修改值
a = 20;
// const两种用法
// 1.const:修饰基本变量p
// 这两种写法是一样的,const只修饰右边的基本变量b
const int b = 20; // b:只读变量
int const b = 20; // b:只读变量
// 不允许修改值
b = 1;
// 2. const:修饰指针变量*p,带*的变量,就是指针变量
// 定义一个指向int类型的指针变量,指向a的地址
int *p = &a;
int c = 10;
// 允许修改p指向的地址
p = &c;
// 允许修改p访问内存空间的值
*p = 20;
// const修饰指针变量访问的内存空间,修饰的是右边*p1,
// 两种方式一样
const int *p1; // *p1:常量 p1:变量
int const *p1; // *p1:常量 p1:变量
// 允许修改p指向的地址
p1 = &a;
// 不允许修改p访问内存空间的值
*p1 = 27;
// const修饰指针变量p1
int * const p2; // *p1:变量 p1:常量
// 不允许修改p指向的地址
p2 = &a;
// 允许修改p访问内存空间的值
*p2 = 27;
// 第一个const修饰*p1 第二个const修饰 p1
// 两种方式一样
const int * const p3; // *p1:常量 p1:常量
int const * const p3; // *p1:常量 p1:常量
// p的指向和指向内存空间的值都不能修改
p3 = &a;
*p3 = 27;
联合使用
static和extern
extern
引用变量时会先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找
static int age = 21;
void test(void) {
static int age = 21;
age++;
NSLog(@"%d", age);
}
int main(void) {
test();
test();
extern int age;
NSLog(@"%d", age);
return 0;
}
// 打印结果:
// 22
// 23
// 21
static和const
声明一个只读的静态变量,常用于声明上面《EOC 2.0》提到的类型常量
// 开发中经常将无需改变的key值作为类型(字符串)常量
static NSString* const key1 = @"keyValue1";
static NSString const * key2 = @"keyValue2";
static const NSString* key3 = @"keyValue3";
extern和const
声明一个全局的类型常量,供多个文件共享
全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个GlobeConst文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找
// GlobeConst.h
extern NSString * const kGlobalConstKey;
// GlobeConst.m
NSString * const kGlobalConst = @"globalConst";
auto关键字
认识了这么多修饰变量的关键字,最后再来简单认识一下之前未了解过的auto
关键字
变量被auto
修饰后,编译器会根据初始化表达式自动推断出变量的类型:
auto x = 3.14; // x 是浮点型 double
auto y = 520; // y 是整形 int
auto z = 'a'; // z 是字符型 char
auto nb; // error,变量必须要初始化
auto double nbl; // 语法错误, 不能修改数据类型
auto pi = new auto(42); // pi 的类型被推导为 int*
const auto& y = x; // y 的类型被推导为 const int&
auto关键字更适用于类型冗长复杂、变量使用范围专一时,使程序更清晰易读。如:
std::vector<int> vect;
for (auto it = vect.begin(); it != vect.end(); ++it)
{ //it的类型是std::vector<int>::iterator
std::cin >> *it;
}
或者保存lambda表达式类型的变量声明:
auto ptr = [](double x){return x*x;};//类型为std::function<double(double)>函数对象