OC 中static、const理解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OC 中static、const理解相关的知识,希望对你有一定的参考价值。
参考技术A 1. static:(1)修饰全局变量: static 修饰则意味着该变量仅在定义此变量的编译单元中可见, 不会导致其他单元重复导致命名冲突, 当编译器编译到此单元时, 就会输出一份 "目标文件"(object file)。可修饰常量、变量、函数,被修饰的数据类型存储在静态区,程序结束被销毁。不管调用多少次,只会初始化一次。
(2)修饰局部变量: 延长局部变量的生命周期, 程序结束才会销毁。只会初始化一次。存储在静态区。
2. const:
被修饰的变量变为常量,存储在常量区,不能被修改。
const NSString * name = @"Rg";使 *name 指针地址不可变, 实际指向内容不受影响, 修改指针地址编译器报错。
NSString const * name = @"Rg";同上面写法一致, 修饰了 name 指针地址使其不可变
NSString * const name = @"Rg";使 *name 指针指向内容不可变, 指针地址不受影响, 修改内容则编译报错。
修饰符 extern/static/const/UIKIT_EXTERN(OC版extern) 的使用
先逐个分析每个修饰符单独代表的含义,然后再分析某些修饰符组合在一起的时候所代表的含义。
const
const 相对最好理解,就是修饰的东西不能再被修改。
没有 const 修饰的指针,指针 p 和 *p 都能被修改:
1 // // 定义一个指针 2 int *p = NULL; 3 // // 定义2 个int 类型的变量 4 5 int a = 10; 6 int b = 30; 7 8 // p 指向 a 9 NSLog(@"p = %p, a = %d, b = %d", p, a, b); // p = 0x0, a = 10, b = 30 10 p = &a; 11 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff52706a14, *p = 10, a = 10, b = 30 // 这里指针 p 本来指向 0x0,现在开始指向 a 的地址 12 *p = 20; 13 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff52706a14, *p = 20, a = 20, b = 30 // 这里指针 p 指向的仍是 a 的地址,这里修改 *p 的值即为修改 p 指向的地址的值,即为修改 a 的值,所以 a 的值也变为 20 14 // p 指向 b 15 p = &b; 16 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff52706a10, *p = 30, a = 20, b = 30 // 这里指针 p 本来指向的是 a 的地址,现在开始指向 b 的地址,所以 *p 的值即为 b 的值,是30 17 *p = 40; 18 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff52706a10, *p = 40, a = 20, b = 40 // 这里指针 p 指向的仍是 b 的地址,这里修改 *p 的值即为修改 p 指向的地址的值,即为修改 b 的值,所以 b 的值也变为 40
使用 const 修饰 *p,即表示指针 p 指向的地址里面存放的值不能再改变,*p 只能在初始化时被赋值一次,以后都不能再被赋值,即不能再被修改,但是指针 p 指向的地址是可以改变的。且 const 只要在 *p 前面,在类型符前面和后面的写法表示的意义都是一样的。
1 // const 修饰的 *p 2 int a = 10; 3 int b = 20; 4 5 const int *p = &a; 6 // int const *p = &a; 这里的这两种写法是一致的 7 NSLog(@" p = %p, *p = %d", p, *p); // p = 0x7fff57553a1c, *p = 10 8 p = &b; 9 // *p = b; // 编译错误: Read-only variable is not assignable 10 NSLog(@" p = %p, *p = %d", p, *p); // p = 0x7fff57553a18, *p = 20 这里指针 p 指向的地址改变了,同时表示的值也是该地址存放的值
使用 const 修饰 p,即表示指针 p 指向的地址不能再改变了, 指针 p 在初始化的时候被一个地址赋值,以后都不能再被赋值,即不能再被修改,但是指针 p 指向的地址里面存放的值是可以改变的。这里和 const 修饰 *p 表示的含义很类似,都保持其中一项不能改变。
1 int a = 10; 2 int b = 20; 3 4 int * const p = &a; 5 // p = &b; // 编译错误: Cannot assign to variable ‘p‘ with const-qualified type ‘int *const‘ 6 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff514caa1c, *p = 10, a = 10, b = 20 7 *p = b; 8 NSLog(@"p = %p, *p = %d, a = %d, b = %d", p, *p, a, b); // p = 0x7fff514caa1c, *p = 20, a = 20, b = 20 // 这里指针 p 指向的地址没有改变,但是该地址下存放的值变为了 20 ,同时 a 的值也变成了20
注意: 如果初始化的时候指针 p 指向 NULL,那其指向的地址是 0x0,通过 *p 修改改地址下存放的值在编译的时候不会报错,但是在运行的时候会直接崩溃,0x0 地址下存放的值是什么不能用 NSLog 打印,运行的时候也会直接崩溃。当没有用 const 修饰 p 时即 p 指向的地址可以改变的时候,可以把 p 由指向 0x0,指向为别的地址。不过好像指针在初始化的时候指向 NULL,貌似也没有什么意义。
1 int * const p = NULL;
static(重点学习)
静态全局变量
在全局变量前,加上关键字 static,该变量就成为了一个静态全局变量。
静态全局变量的特点:
1.该变量在全局数据区分配内存
2.在 OC 中,未被初始化的 NSString 类型的静态全局变量会被自动初始化为 null,指向 0x0 地址。
3.在 OC 中,在一个类的 .h 或者 .m 文件中定义的静态全局变量在它的整个 .m 文件里面都是可见的,当在另一个类中引入该类时,是能直接访问上一个类的 .h 里面定义的静态全局变量的,且该静态全局变量指向的地址和值仍是上一个类的 .h 文件初始化时的地址和值,即使在上一个类的 .m 里面对该静态全局变量赋了新值,如果去除 static 修饰,访问上一个类里面的全局变量会直接 Apple Mach-0 Linker Error。
4.在 OC 中,在一个类的 .h 中定义的静态全局变量并不属于该类的某个对象,只要引入该类,就能访问它的静态局部变量,并不需要创建该类的对象。静态全局变量时属于该类的,不是属于某个类对象的。
在 ViewController.h 定义了一个静态全局变量 tempStr:
1 static NSString *tempStr = @"HML";
在 ViewController.m 里面的把它赋新值:
1 NSLog(@"%@ %@ %p", self, tempStr, tempStr); 2 3 tempStr = @"CHM"; 4 NSLog(@"%@ %@ %p", self, tempStr, tempStr);
1 2017-05-18 21:38:06.083 UIKIT_EXTERN(OC版extern) 的使用[20843:2940956] <ViewController: 0x7fcd93d0bf10> HML 0x106e62068 2 2017-05-18 21:38:06.083 UIKIT_EXTERN(OC版extern) 的使用[20843:2940956] <ViewController: 0x7fcd93d0bf10> CHM 0x106e620c8
在 ViewController.m 里面加一个按钮,点击跳转到 TwoViewController,这时在 TwoViewController 里面打印 ViewController 的静态全局变量 tempStr:
1 NSLog(@"%@ %@ %p", self, tempStr, tempStr);
1 2017-05-18 21:38:11.847 UIKIT_EXTERN(OC版extern) 的使用[20843:2940956] <TwoViewController: 0x7fcd93e353d0> HML 0x106e62068
在 TwoViewController 里面修改该静态全局变量并打印:
1 tempStr = @"LOVE YOU";
1 2017-05-18 21:38:11.847 UIKIT_EXTERN(OC版extern) 的使用[20843:2940956] <TwoViewController: 0x7fcd93e353d0> LOVE YOU 0x106e620e8
点击 TwoViewController 左上角的返回,在 ViewController 的 viewDidAppear 里面打印该静态全局变量:
1 - (void)viewDidAppear:(BOOL)animated { 2 [super viewDidAppear:animated]; 3 NSLog(@"%@ %@ %p", self, tempStr, tempStr); 4 }
1 2017-05-18 21:38:21.277 UIKIT_EXTERN(OC版extern) 的使用[20843:2940956] <ViewController: 0x7fcd93d0bf10> CHM 0x106e620c8
打印结果就是这样,在屋里走了一圈,不知道为啥,那些赋的新值都到了哪去。
静态变量的内存位置?
静态变量(static 修饰的变量)都在全局数据区分配内存,包括静态全局变量和后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下:
一般程序把新产生的动态数据放在堆区,函数内部的自动变量 (自动变量,只在定义它们的时候才创建,在定义它们的函数返回时系统回收变量所占存储空间。对这些变量存储空间的分配和回收是由系统自动完成的。一般情况下,不作专门说明的局部变量,均是自动变量。自动变量也可用关键字auto作出说明。自动变量只有一种存储方式,就是存储在栈中。由于自动变量存储在栈中,所以自动变量的作用域只在函数内,其生命周期也只持续到函数调用的结束。这个过程是通过一个堆栈的机制来实现的。为自动变量分配内存就压栈,而函数返回时就退栈。) 放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。
静态局部变量
在全局变量前,加上关键字 static,该变量就成为了一个静态局部变量。
静态局部变量的特点:
1.该变量在全局数据区分配内存
2.静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。
3.静态局部变量一般在声明处初始化,如果没有显式初始化,在 OC 中,未被初始化的 NSString 类型的静态局部变量会被自动初始化为 null,指向 0x0 地址。
4.静态局部变量始终驻留在全局数据区,直到程序运行结束,但其作用域为局部作用域,当定义它的函数或者语句块结束时,其作用域也随之结束。
通常,在函数内部定义一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存,但随着该函数执行完毕,系统就会回收栈内存,局部变量就会释放。但有时候需要在两次调用之间对变量的值进行保存。
为什么要引入 static ?
函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题:如果想将函数中此变量的值保存至下一次调用时,如何实现?最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。
什么时候用 static ?
需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。
static 内部机制?
静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
static 优势?
可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
在静态数据区,内存中所有的字节默认值都是0x00。
static 问答?
1. static 全局变量与普通的全局变量有什么区别 ?
全局变量(外部变量)的说明之前再冠以 static 就构成了静态的全局变量。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static 全局变量只初使化一次,防止在其他文件单元中被引用;
2. static 局部变量和普通局部变量有什么区别 ?
把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static 局部变量只被初始化一次,下一次依据上一次结果值;
3. static 函数与普通函数有什么区别?
static 函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static 修饰的函数),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.
static 函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
extern
如果同时要在多个 .m 文件里面用到值相同(初值相同且不可改变)的且命名相同的一个变量(忽略PCH 文件中宏定义的使用),就比如使用一个 myURL 的变量:
1 NSString * const myURL = @"http://xxx.xxx.xxx.xxx";
建立了三个类 ViewController、TwoViewController、ThreeViewController,分别在它们的 .m 文件里面像上面定义 myURL,编译时直接报 myURL 重复定义:
1 duplicate symbol _myURL in: 2 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 3 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ThreeViewController.o 4 duplicate symbol _myURL in: 5 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 6 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/TwoViewController.o 7 ld: 2 duplicate symbols for architecture x86_64 8 clang: error: linker command failed with exit code 1 (use -v to see invocation)
如果把 myURL 的定义放在 PCH 文件里面,就相当于每一个 .m 文件中都写了同样的一句代码,这时 myURL 的重复定义会在所有文件中出现:
1 duplicate symbol _myURL in: 2 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 3 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/AppDelegate.o 4 duplicate symbol _myURL in: 5 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 6 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/Person.o 7 duplicate symbol _myURL in: 8 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 9 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ThreeViewController.o 10 duplicate symbol _myURL in: 11 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 12 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/TwoViewController.o 13 duplicate symbol _myURL in: 14 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/ViewController.o 15 /Users/jay/Library/Developer/Xcode/DerivedData/UIKIT_EXTERN(OC版extern)_的使用-gorhoqfprjpoemhcpaqgclagloav/Build/Intermediates/UIKIT_EXTERN(OC版extern) 的使用.build/Debug-iphonesimulator/UIKIT_EXTERN(OC版extern) 的使用.build/Objects-normal/x86_64/main.o 16 ld: 5 duplicate symbols for architecture x86_64 17 clang: error: linker command failed with exit code 1 (use -v to see invocation)
参考链接:http://blog.csdn.net/yanhsheng304/article/details/50508894
http://blog.csdn.net/xpwang168/article/details/8087143
http://blog.csdn.net/hdfqq188816190/article/details/51435268
http://baike.baidu.com/item/自动变量
http://blog.csdn.net/keyeagle/article/details/6708077/
以上是关于OC 中static、const理解的主要内容,如果未能解决你的问题,请参考以下文章
const,volatile,static,typdef,几个关键字辨析和理解