Const_Static_Extern
在 Effective Objective-C 中 第 4 条,说要少用 #define
多用常量 ,这里就聊聊 const
static
extern
这些关键字
const
关于常量,我们有以下几种定义方式
1 | int one = 1; |
我们知道当我们声明一个指针的时候
1 | int one = 1; |
其实 i 本身有一个地址 A ,而 i 这个变量存储了一个指针,这个指针指向了 one 的地址,也就是 B,而这个地址中存放的是 2。
其实简单的来说,const 的位置
,决定了 A 是 const 还是 B 是 const,下边逐一分析。
👉int const *i 和 const int *i
关于 1 和 2 其实是一样的,都等于 int const* i
,也就是 *i
是被 const 修饰的,我们无法再次改变 *i
,但我们可以改变 i
。也就是我们无法 *i = two
但我们可以 i = &two
1 | int one = 1; |
👉int * const i
这种情况来说,i
是被 const 修饰的,我们是可以改变 *i
,而无法改变 i
。也就是我们可以 *i = two
但无法 i = &two
1 | int one = 1; |
👉const int * const i
这种情况就比较简单了,我们无法改变 *i
,也无法改变 i
的地址。也就是我们无法 *i = two
,也无法 i = &two
1 | int one = 1; |
👉再看
我们来看一个有意思的事情
1 | int a = 10; |
想想在往下看
1 | 1. 0x7ffee643b908 |
怎么样,和你想的一样么,我们发现 a 被偷偷的改成了 11,这是为什么,我们分析下地址就知道了。
我们先看下地址
我们知道 *p
是取地址的内容,当我们在执行 *p = b
的时候,其实就是 取 0x7ffee643b90c 的内容,并修改为 11
,而这个叫 a 的变量,他的地址其实还是原来的。当我们输出 a 的时候,a 变成了 11。如果我们想改变 *p
的值,安全的做法,是 p = &someValue
而 不要
使用 *p = someValue
所以当我们 int const *p
的时候,我们是无法修改 *p
的值的。
当我们知道一个数据的地址的时候,如何取地址中存放的数据呢,例如上边有个地址是 0x7ffee643b90c,如何知道 这个地址存放的是什么。 可以这样做 *(int *)(0x7ffee643b90c)
这样,就能取到这个地址中存放的数据了,但是前提是你需要知道这个地址中的数据是什么类型的,不然取出来,也是不正确的。
在 OC 中,其实是一样的 当我们创建一个对象 NSObject *obj1 = [NSObject new]
的时候, obj1 是一个地址,指向了真正的系统分配的内存空间,当我们用 NSObject * const obj1
来修饰的时候,我们的 obj1 就再也无法被赋值为其他值了,因为这个 obj1 是被 const 修饰的,而 *obj1 是可以被改的,有兴趣可以试试。
👉小结
关于 const 的位置,我们可以这么理解,const 后边的哪个东西,就是我们修饰的常量,是不可变的。当 int const* p
的时候,也就是这个 *p
是常量,我们无法改变他,而当 int * const p
的时候,这个 p
是常量,我们无法改变他。
static
static 有以下几点特性
👉内部可见
在 OC 中,当在我们在 .m 中写一个被 static 修饰的变量或者函数的时候,我们只能在 这个文件
中使用,而不能再别的地方调用。在其他地方调用,将会提示,找不到该定义。但如果在 .h 中定义的话,那么是可以被外部使用的。
👉只初始化一次
如下代码,static 修饰的 count 只会初始化一次,每次调用 count 会累加,而 不用 static 修饰的话,每次会重新初始化
1 | int fun() |
👉存储在 data segment 中
被 static 修饰的存储在 data segment
,而在函数中,没有被 static 修饰的,存放在 stack segment
,更详细的分配,可以参见这篇文章
👉未初始化自动赋值为默认值
被 static 修饰的变量,如果没有指定值,将会被赋值为默认值
1 | static int x ; |
👉只能是特定值,不能是函数
static 修饰的只能被赋值为特定的值,而不能是函数
1 | static int x = initializer(); //error: Initializer element is not a compile-time constant |
extern
这个就比较简单了,就是一个标记,外部可用,当我们在 .m 中声明一个常量的时候,需要供外部使用就需要这么写。在 Foundation
框架中,我们经常能看到这种写法。
1 |
|
inline
inline 编译器标识符相当于在执行这段代码的时候,把这段代码直接拿过来,而不是像调用函数那样直接调用(函数会跳转,有一些开销)。
1 |
|