OC中经常用到单例。创建一个单例的方法是这样的(这里随便举了一个自己写过的单例作为示例):
+ (instancetype)shareInstance{
static ICCookieHelper *cookieHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cookieHelper = [[ICCookieHelper alloc]init];
});
return cookieHelper;
}
以前只知道单例是这样写的,dispatch_once函数保证cookieHelper变量在整个app生命周期中只执行一次赋值操作,这是实现单例的关键所在。然后最近突然把目标聚焦在了不是很显眼的地方
static ICCookieHelper *cookieHelper = nil;
由于shareInstance方法会被多次调用,每次执行该方法,上面语句调用一次(在我未搞清楚static的作用之前我是这么认为的),cookieHelper变量似乎又被重新定义了,且无初始值。但是赋值语句只执行一次啊,这样一来cookieHelper不就是nil了!!我一下子就懵逼了。我知道肯定是这个static耍了什么把戏,于是开始查资料…
变量,包括局部变量个全局变量,又根据有无static修饰,分为静态变量和非静态变量。因此就有了非静态局部变量、静态局部变量、非静态全局变量、静态全局变量。但不管怎样,都是变量没的说!因此先来说说变量的所有的共性:生命周期。
一个变量,其生命周期无非从创建开始到撤销结束。其中的相关概念包括变量定义、初始化、赋值。
下面代码对变量的使用过程做了测试,打印结果符合预期
int a; //定义一个int变量,系统为其分配内存地址,但变量值是不确定的
NSLog(@"%d",a); //一个不确定数
NSLog(@"%p",&a); //变量地址
NSLog(@"********");
static int *b; //定义一个指针变量,系统为指针变量分配内存地址,该地址存放指针变量的地址,并不是变量本身所代表的地址,而指针变量本身所代表的地址是一个空地址(空指针)。这里把指针变量看成是普通变量就能理解为什么变量本身值是空的了。
NSLog(@"%p",b); //指针变量本身值为0x0
NSLog(@"%p",&b); //指针变量地址(并非变量本身所代表的地址,而是存放指针变量的地址)
NSLog(@"********");
a = 10; //对普通变量a赋值
NSLog(@"%d",a); //10
NSLog(@"%p",&a); //a的地址没有改变
NSLog(@"********");
b = &a; //对指针变量b赋值,把a的地址赋给b
NSLog(@"%p",b); //&a
NSLog(@"%p",&b); //b的地址没有改变
NSLog(@"********");
MyObject *myObject; //定义一个对象,对象是一个指针变量
NSLog(@"%@",myObject); //指针变量所指向的值为空 (null)
NSLog(@"%p",myObject); //指针变量本身值为0x0
NSLog(@"%p",&myObject); //指针变量的地址 0x7fff5fbff828
myObject = [[MyObject alloc]init]; //指针变量初始化
NSLog(@"%@",myObject); //<MyObject: 0x100400110>
NSLog(@"%p",myObject); //0x100400110 <MyObject: 0x100400110>的地址
NSLog(@"%p",&myObject); //0x7fff5fbff828 0x100400110的地址
myObject = [[MyObject alloc]init]; //重新赋值
NSLog(@"%@",myObject); //指针所指的值改变
NSLog(@"%p",myObject); //指针变量的值改变
NSLog(@"%p",&myObject); //指针变量的地址不变
所谓静态变量,即被static修饰的变量。没有被static修饰的变量,称之非静态变量,又称自动变量,事实上它缺省被auto修饰。
局部变量定义在一个函数内部,生存范围为变量定义所在函数或模块,以“{”“}”界定,该类型变量只能在函数(模块)内被调用。
非静态局部变量即变量即是局部的,又是非静态的。变量在定义的时候被创建,当程序运行出了函数(模块),变量就被撤销。当下次再次调用该函数(模块)时,重新创建的变量和之前的那个没有半毛钱关系。
与非静态局部变量相比,静态局部变量有着很大不同。静态局部变量属于静态存储方式,存放在内存的静态存储区,该内存区域中的变量在整个程序生命周期中地址都是不变的。静态局部变量具有以下特点:
根据静态局部变量的特点,可以看出它是一种生存期为整个源程序的变量。虽然离开定义它的函数后不能使用,但如果再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。
非静态全局变量也属于静态存储方式,因此它的生命周期也是整个程序的生命周期。
如果对于一个变量,想让它的作用于不局限与当前函数,而是想在其他函数,甚至在其他文件中都能使用改变,这时候就要把变量定义成全局变量。注意如果定义成静态类型的,那么其作用于只限于当前文件,不能别其他文件所共享。
如何跨文件使用非静态全局变量呢?下面做简单示例:
#import "MyFile.h"
int a = 10;
@implementation MyFile
@end
在MyFile.m文件中创建并初始化了一个全局变量,下面代码在main.m文件中通过extern关键字引用该变量
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
extern int a;
NSLog(@"%d",a); //打印结果 10
return 0;
}
成功在main.m文件中引用了MyFile.m中定义的全局变量a。注意:这里称这个过程为引用是正确的,extern关键的作用就是引用一个外部定义的全局变量,并没有定义一个新的变量。不信的话在不同的地方引用同一个变量,分别打印地址,会发现是同一个地址,也就是说始终只有一个变量,只是在不同的地方使用它罢了(因此在多线程环境中要小心使用)。
另外,无需在引用全局变量的文件中导入定义全局变量所在文件的头文件,全局变量编译时就被定义好了。事实上,如果在一个.h问价中定义了一个局部变量,另一个文件中又导入该文件就会报重复错误,因此局部变量的定义通常都放在.m文件中。
首先静态全局变量肯定也属于静态存储方式,生命周期为整个程序。另外静态全局变量与非静态全局变量的区别,其实前面以及说过了。两个的唯一区别在于,非静态全局变量的作用域是整个工程,即在别的文件中也可以引用;静态全局变量的作用范围只限于当前文件,在同一源程序的其他文件中不能直接引用。因此要想在别的文件中操作当前文件的静态全局变量,只能在当前文件中定义一个接口函数。
把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生命周期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。