在C语言中,程序内变量或函数的作用域和寿命是由其存储类决定的。每个变量都有其生命周期,或存储其值的上下文。函数和变量一样,存在于一个特定的范围或可见域里,这决定了哪一部分程序知道切能够访问它们。
C里有四种存储类:
- auto
- register
- static
- extern
auto
auto是默认存储类,因此通常不需要显示地使用。当程序运行到相应代码块时,auto类型的变量能自动分配内存,并且在该程序块运行完成时释放。访问auto变量仅限于在声明它们的block,以及任何嵌套的block内。
register
绝大多数Objective-C程序员可能也不熟悉register,因为它没有被广泛的使用在NS世界里。
register行为就像auto,但不同的是它们不是被分配到堆栈中,它们被存储在一个寄存器里。
寄存器能比内存提供更快的访问速度,但由于内存管理的复杂性,把变量放在寄存器中并不能保证程序变得更快。事实上,很可能由于在寄存器上占用了不必要的空间而最终被放缓执行。使用寄存器实际上只是一个给编译器存储变量的建议,实现时可以选择是否遵从这一点。
建议是最好不要使用register,因为比起其他任何明显的方式上加快应用程序,它更容易引起让人更加头疼的结果。
static
作为关键字,static有多重不同的用途。当涉及到存储类时,static意味着以下两件事情之一:
- 方法或函数内部的一个static变量保留其调用之间的值。
- 全局声明的一个static变量或方法可以被任何函数或方法调用,只要这些方法出现在跟static变量或方法同一个文件中。
静态单例(Static Singletons)
Objective-C 中一个常见的模式是静态单例,在这个case里,一个静态声明的变量被初始化,并在任何一个函数或类方法中被返回。 dispatch once
用于保证变量初始化在一个线程安全的方式下只发生一次:
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
单例模式对于创建整个应用程序共享的对象是很有用的,诸如HTTP客户端或一个通知管理,或创建过程很昂贵的对象,诸如格式化。
extern
static使得一个特定的文件中的函数和变量全局可见,extern则使它们对所有文件可见。
一般来说,全局变量并不是一个好主意。由于没有如何以及何时改变值得限制,常常会导致一些无法调试的bug。在Objective-C,对extern有两个常见和实际的用途。
全局字符串常量
任何时候,如果你的应用程序要在一个公共头文件申明一个非自然语言的字符串常量,都应该将其声明为外部字符串常量。尤其是在声明诸如userInfo字典,NSNotification名称和NSError域的时候。
该模式是在公共头文件里申明一个extern的NSString * const
,并在实现文件里定义该NSString * const
:
AppDelegate.h
extern NSString * const kAppErrorDomain;
AppDelegate.m
NSString * const kAppErrorDomain = @"com.example.yourapp.error";
字符串的值并没有特别的需要注意的事情,只要它是唯一的。使用字符串常量建立了严格的约束,用该常量来代替字符串本身。
公共方法
一些 API 可能会想要公开曝光一些辅助方法。出于仅提供辅助而与具体状态无关的考虑,用方法来封装这些行为是一个很好的方式,而且如果特别有用,还可能值得使其全局可用。该模式例子如下:
TransactionStateMachine.h
typedef NS_ENUM(NSUInteger, TransactionState) {
TransactionOpened,
TransactionPending,
TransactionClosed,
};
extern NSString * NSStringFromTransactionState(TransactionState state);
TransactionStateMachine.m
NSString * NSStringFromTransactionState(TransactionState state) {
switch (state) {
case TransactionOpened:
return @"Opened";
case TransactionPending:
return @"Pending";
case TransactionClosed:
return @"Closed";
default:
return nil;
}
}