Fork me on GitHub
An Guoli's Blog

OC里的const/static/extern、UIKIT_EXTERN、#define、#ifdef/#else/#endif

宏的常见用法:

  1. 常见字符串抽成宏
  2. 常见代码抽成宏

const(常量)的作用:

当有字符串常量的时候,苹果推荐我们使用const,苹果经常把常用字符串定义成const

宏与const的区别

  • 编译时刻:宏是预编译(编译之前处理),const是编译阶段。

  • 编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。

  • 宏的好处:宏能定义一些函数,方法。 const不能。

  • 宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。

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

const

  • 1.const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p,对象变量)
  • 2.被const修饰的变量是只读的。

const的简单使用

首先我们要复习一下C语言的指针

1
2
3
4
5
6
7
8
9
// 定义int变量
int a = 66;
int b = 88;
// 定义指向a的指针变量
int *p=&a;
// 修改p的地址
p = &b;
*p = 30;
NSLog(@"%d",b);

请问打印值为多少?

首先p是指向a的指针,p保存的是a的地址,p=&b时,p变成的指向b的指针,所以*p=30,其实就是给b附值,所以打印结果为30。

写下来要使用const了

1
2
3
4
5
// 用const修饰指针变量
int * const p = &a;
此时
(1) p=&b;
(2) *p=30;

请问1和2哪个会报错?
上面解释了const是修饰右边的变量是只读的,所以说 p是只读的那么1就会报错。

1
2
3
4
const int * p = &a;
此时
(1) p=&b;
(2) *p=30;

请问1和2哪个会报错?

同理 所以说 p是只读的那么2就会报错。
其实 const int * p = &a; 与 int const p = &a; 是等价的

有两个更变态的写法

1
2
int const * const p = &a; // *p:只读 p:只读
const int * const p = &a; // *p:只读 p:只读

不用说大家也理解了吧

现在大家明白了const的作用吧,点开苹果头文件可以开到很多这样的写法,现在你是不是可以理解苹果为什么要这样做了吗?

1
UIKIT_EXTERN NSString *const UIViewControllerHierarchyInconsistencyException NS_AVAILABLE_IOS(5_0);

那我们继续学习const的用法吧

const开发中使用场景:

  • 1.当一个方法参数只读
  • 2.定义只读全局变量

很多时候我们在开发中都要static和extern与const联合使用,所以接下来学习一下static和extern和的作用

static作用

参考: http://blog.yoonangel.com/2018/03/06/local-and-global-variables-in-OC/

对于全局变量来说,static 改变了其作用域;对于局部变量来说,static改变了其存储方式,从而改变了生命周期

  • 修饰局部变量:
  1. 延长局部变量的生命周期,程序结束才会销毁。
  2. 静态局部变量只会生成一份内存,只会初始化一次。
  3. 不会改变局部变量的作用域
  • 修饰全局变量
  1. 只能在本文件中访问,修改全局变量的作用域,生命周期不会改。

修饰局部变量

1
2
3
4
5
for (int i=0; i<10; i++) {
int a = 0;
a++;
NSLog(@"%d",a);
}

请问输出结果为多少?每次进入大括号内都会重新初始化 a 所以结果为 1。

如果加上static呢

1
2
3
4
5
for (int i=0; i<10; i++) {
static int a = 0;
a++;
NSLog(@"%d",a);
}

此时的请问输出结果为多少?

static:修饰的变量,程序一运行就会分配一次内存,用static修饰的代码,只会在程序一启动就会执行,以后就不会在执行,所以结果为1~10。

extern作用:

这个单词翻译过来是“外面的、外部的”。顾名思义,它的作用是声明外部全局变量。这里需要特别注意extern只能声明,不能用于实现,而且定义和分配内存都在原来类中。

声明一个变量,不能定义变量

注意:extern修饰的变量不能初始化

使用场景,一般用于声明全局变量

static与const联合使用

  • static与const作用:声明一个只读的静态变量

  • 开发使用场景:在一个文件中经常使用的字符串常量,可以使用static与const组合

1
static NSString * const key = @"name";

所以大家要记住:staic和const联合的作用:声明一个在本文件中静态的全局只读常量

iOS中staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量。

extern与const联合使用

开发中使用场景:在多个文件中经常使用的同一个字符串常量,可以使用extern与const组合。

  • 原因:

    • static与const组合:在每个文件都需要定义一份静态全局变量。
    • extern与const组合:只需要定义一份全局变量,多个文件共享。
  • 全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个全局的文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找。

比如我们在新建一个文件,只留.m里#import ,其他的都删除了

在.m里定义

1
2
3
4
5
6
7
NSString * const name = @"zhanming";
NSString * const name1 = @"zhanming";
NSString * const name2 = @"zhanming";
NSString * const name3 = @"zhanming";

在.h里引用

1
2
3
4
5
6
7
extern NSString * const name ;
extern NSString * const name1 ;
extern NSString * const name2 ;
extern NSString * const name3 ;

这样就可以在程序里引用了哈哈,可是我们点开苹果的头文件发现

1
UIKIT_EXTERN NSString *const UIViewControllerShowDetailTargetDidChangeNotification NS_AVAILABLE_IOS(8_0);

苹果用了UIKIT_EXTERN这是什么的不要担心我们点进去看看发现

1
#define UIKIT_EXTERN extern __attribute__((visibility ("default")))

居然就是extern

把我们的在.h里extern换成UIKIT_EXTERN

1
2
3
4
5
6
7
UIKIT_EXTERN NSString * const name ;
UIKIT_EXTERN NSString * const name1 ;
UIKIT_EXTERN NSString * const name2 ;
UIKIT_EXTERN NSString * const name3 ;

最后提醒:在iOS开发中请尽量多使用const、enum来代替宏定义(#define),随着项目工程的逐渐增大,过多的宏定义还可能影响项目的编译速度.

const变量与define宏定义的区别

  • (1) 编译器处理方式不同

    define宏是在预处理阶段展开。
    const变量在编译时确定其值。

  • (2) 类型和安全检查不同

    define宏没有类型,不做任何类型检查,仅仅是展开。
    const变量有具体的类型,在编译阶段会执行类型检查。

  • (3) 存储方式不同

    define宏仅仅是展开,有多少地方使用,就展开多少次,宏本身不会分配内存,宏展开后的立即数是会占用内存的。(展开多少次就分配多少次内存)

    编译器通常不为普通const变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,编译时对其进行类似于宏定义的操作,在出现const变量的地方就替换成其对应的值,没有了存储与读内存的操作,使得它的效率也很高。只有在对const变量使用extern关键字或者取地址操作时才会为它分配存储空间,且const变量从汇编的角度来看,操作const变量时只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const变量在程序运行过程中只有一份拷贝,不会多次分配存储空间。

define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)

const常量会在内存中分配(可以是堆中也可以是栈中)。

  1. 简单的例子:
1
2
3
4
5
6
7
//定义变量
int const age = 100;
const float height = 200;
//若修改变量的值
age = 101;//编译报错
height = 201;//编译报错
  1. 使用extern定义项目的全局常变量

.h文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
项目的全局变量
*/
@interface MenuOrderExtern : NSObject
//下面定义的指针,“值”(内容)是可以改变的,因为const此时修饰的是指针p,而不是指针内容。
//UIKIT_EXTERN NSString const *orderTypeBreakfast;
//UIKIT_EXTERN const NSString *orderTypeBreakfast;
//const,修饰的是它右边的内容。此时修饰的是变量本身,所以值不可被改变。
//所以,需要定义全局常变量而且防止被无意修改,下面也是苹果官方写法:
//UIKIT_EXTERN =extern,此处的作用是为了阅读代码的人知道,别处有此引用,写不写对编译器没关系
UIKIT_EXTERN NSString *const orderTypeBreakfast;
UIKIT_EXTERN NSString *const orderTypeLunch;
UIKIT_EXTERN NSString *const orderTypeDinner;
@end

.m文件

1
2
3
4
5
6
7
@implementation MenuOrderExtern
//定义实质的内容
NSString *const orderTypeBreakfast = @"breakfast";
NSString *const orderTypeLunch = @"lunch";
NSString *const orderTypeDinner = @"dinner";
@end

如何使用

1
2
//外部.m文件引用
extern NSString *const orderTypeBreakfast;

https://www.jianshu.com/p/89656ccfe464

参考

iOS中宏与const的区别与const的使用详解

const,extern

iOS 关键字const/static/extern、UIKIT_EXTERN区别和用法