參考:C Storage Classes
iOS定義靜態(tài)變量漱受、靜態(tài)常量络凿、全局變量
iOS開發(fā)——OC篇&常用關(guān)鍵字的使用與區(qū)別
iOS 宏(define)與常量(const)的正確使用
一、C語言的存儲類型
在C語言中昂羡,程序內(nèi)變量或函數(shù)的作用域和壽命是由其存儲類確定的絮记。每個變量都具有生存周期,或存儲其值得上下文虐先。方法怨愤,同變量一樣,也存在蛹批,或可見于撰洗,一個特殊的范圍里,這就決定了哪一部分能夠知道且能夠訪問它們腐芍。
C里有四種存儲類:
- auto
- register
- static
- extern
1差导、auto
很有可能你從來沒見過這個關(guān)鍵字。這是因為auto是默認(rèn)存儲類猪勇,因此通常并不需要顯式的使用设褐。
當(dāng)運(yùn)行到程序塊的時候,auto類型的變量能自動分配內(nèi)存,并且在該程序塊運(yùn)行完成時釋放助析。 訪問auto變量僅限于聲明它的block犀被,以及任何嵌套block內(nèi)。
2外冀、register
大多數(shù)OC程序員可能也不熟悉register寡键,因為它沒有被廣泛的使用在NS世界里。
register行為就像auto锥惋,但不同的是它們不是被分配到堆棧中昌腰,它們被存儲在一個寄存器里开伏。
寄存器能比內(nèi)存提供更快的訪問速度膀跌,但由于內(nèi)存管理的復(fù)雜性,把變量放在寄存器中并不能保證程序變得更快固灵。事實上,很可能由于在寄存器上占用了不必要的空間而最終被放緩執(zhí)行。使用寄存器實際上只是一個給編譯器存儲變量的建議捏卓,實現(xiàn)時可以選擇是否遵從這一點逗栽。
寄存器在OC不夠普及其實挺好的:最好還是不要使用它,因為比起其他任何明顯的方式加快應(yīng)用程序仍秤,它更容易引起讓人更加頭疼的結(jié)果熄诡。
3、static
作為關(guān)鍵字诗力,static被以很多不同的凰浮,不兼容的方式使用,因此要弄清楚每一個實例到底是什么意思可能會造成混淆苇本。
方法或函數(shù)內(nèi)部的一個static變量保留其調(diào)用之前的值袜茧。
-
全局聲明的一個static變量可以被任何函數(shù)或方法調(diào)用,只要這些方法出現(xiàn)在跟static變量同一個文件中瓣窄。這同樣適用于static方法笛厦。
靜態(tài)單例
OC中一個常見的模式是靜態(tài)單例,在這個case里俺夕,一個靜態(tài)聲明的變量被初始化裳凸,并在任何一個函數(shù)或者類方法中被返回。 dispatch once 用于保證變量初始化在一個線程安全的方式下 只 發(fā)生一次:
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
單例模式對于創(chuàng)建整個應(yīng)用程序共享的對象是很有用的劝贸,諸如HTTP客戶端或一個通知管理登舞,或創(chuàng)建過程很昂貴的對象,諸如格式化悬荣。
4菠秒、extern
當(dāng)static使得一個特定的文件中的函數(shù)和變量全局可見,extern則使它們對所有文件可見。
一般來說践叠,全局變量并不是一個好主意言缤。由于沒有如何及何時改變只的任何對extern有兩個常見和實際的用途。
全局字符串常量
任何時候禁灼,如果你的應(yīng)用程序要在一個公共文件頭部申明一個非自然語言的字符串常量管挟,都應(yīng)該將其聲明為外部字符串常量。尤其是在聲明諸如userInfo字典弄捕,NSNotification 名稱和 NSError 域的時候僻孝。
該模式是在公共頭文件里申明一個extern的NSSting*const,并在實現(xiàn)文件里定義該NSString * const:
//AppDelegate.h
extern NSString * const kAppErrorDomain;
//AppDelegate.m
NSString * const kAppErrorDomain = @"com.example.yourapp.error";
字符串的值并沒有特別需要注意的事情守谓,只要它是唯一的穿铆。使用字符串常量建立了嚴(yán)格的約束,用該常數(shù)變量來代替字符串文本值本身斋荞。
公共方法
一些API可能會想要公開曝光一些輔助方法荞雏。由于僅提供輔助而與具體狀態(tài)無關(guān)的考慮,用方法來封裝這些行為是一個很好的方式平酿,而且如果特別有用凤优,還可能值得使其全局可用。
//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; }}
理解任何事情其實都是去了解上下文蜈彼≈妫可能那些我們看到的很明顯且不證自明的東西,對所有那些沒有我們的參照系的人來說是未知的幸逆。我們無法真正了解和欣賞自己和他人的觀點及信息的差異或許使我們最根本的缺點棍辕。
這就是為什么,在我們構(gòu)建的邏輯0和1的宇宙中秉颗,我們?nèi)绱酥?jǐn)慎的區(qū)分上下文痢毒,并基于這些明確的規(guī)則上構(gòu)建我們的假設(shè)。C存儲類對于理解程序是如何運(yùn)行時必不可少的蚕甥。如果沒有他們哪替,我們的開發(fā)將如履薄冰。因此菇怀,需要謹(jǐn)慎對待這些簡單的規(guī)則凭舶,才能包含新新的編寫代碼。
二爱沟、iOS的內(nèi)存存儲區(qū)的劃分
- 1帅霜、棧區(qū):棧區(qū)主要存放函數(shù)內(nèi)部定義的變量、數(shù)組呼伸。函數(shù)調(diào)用時身冀,開辟空間钝尸,函數(shù)執(zhí)行完畢,回收空間搂根,空間的開辟與回收有系統(tǒng)管理珍促。
- 2、堆區(qū):堆區(qū)最大的特點:空間的開辟與釋放由開發(fā)人員手動管理剩愧。
- 3猪叙、全局靜態(tài)區(qū):主要存放函數(shù)外部定義的全局變量以及靜態(tài)變量,空間一旦開辟仁卷,就不會回收穴翩,直到應(yīng)用程序執(zhí)行結(jié)束。
- 4锦积、常量區(qū):存儲常量:整形常量芒帕、浮點型常量、字符串常量
- 5充包、代碼區(qū):存放程序編譯之后生成的cpu指令副签。
三遥椿、OC中的一些關(guān)鍵字
1基矮、static
1.1 static變量只是在編譯時候進(jìn)行初始化,對于static變量冠场,無論是定義在方法體里面還是在方法體外面其作用域都一樣家浇。
我們經(jīng)常使用的UItableViewController里面,在定義UItableView的時候碴裙,模板會經(jīng)常使用以下代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
在上面定義了static變量钢悲,在編譯期間會對這個變量進(jìn)行初始化賦值,也就是說這個變量值要么為nil舔株,要么在編譯期就可以確定其值莺琳,一般情況下,只能用NSSrting或者基本類型载慈,并且這個變量只能在cellForRowAtIndexPath訪問惭等。這個和C語言里面的static的變量屬性一樣。
1.2 static變量也可以定義在.m的方法體外办铡。這樣所有的方法內(nèi)部都可以訪問這個變量辞做。但是在類之外是沒有辦法訪問的,也就是不能用 XXXClass.staticVar 的方式來訪問 staticVar變量寡具。相當(dāng)于static變量都是私有的秤茅。
如果.m文件和方法體里面定義了同名的static變量,那么方法體里面的實例變量和全局的static變量不會沖突童叠,在方法體內(nèi)部訪問的static變量和全局的static變量是不同的框喳。
@implementation IFoundAppDelegate
static NSString * staticStr = @"test";
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
static NSString * staticStr = @"test2";
NSLog(@"the staticStr is %@ -- %d",staticStr,[staticStr hash]);
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(@"the staticStr is %@ -- %d",staticStr,[staticStr hash]);
}
以上兩個static變量是兩個不同的變量,在didFinishLaunchingWithOptions方法內(nèi)部,訪問的是方法體內(nèi)部定義的staticStr變量五垮,在applicationWillResignActive方法體里面撰豺,訪問的是全局定義的staticStr變量∑从啵可以通過日志打印其hash來進(jìn)行確認(rèn)兩個變量是否一樣污桦。
2、const和define
2.1匙监、當(dāng)我們想全局共用一些數(shù)據(jù)時凡橱,可以用宏、變量亭姥、常量
宏:
#define HSCoder @"漢斯哈哈哈"
變量:
NSString *HSCoder = @"漢斯哈哈哈";
常量:
//四種寫法:
static const NSString *HSCoder = @"漢斯哈哈哈";
const NSString *HSCoder = @"漢斯哈哈哈";
NSString const *HSCoder = @"漢斯哈哈哈";
NSString * const HSCoder = @"漢斯哈哈哈";
思考:宏與常/變量的選擇稼钩?
- 宏:只是預(yù)處理器里進(jìn)行文本替換,沒有類型达罗,不做任何類型檢查坝撑,編譯器可以對相同的字符串進(jìn)行優(yōu)化。只保存一份到 .rodata 段粮揉。甚至有相同后綴的字符串也可以優(yōu)化巡李,你可以用GCC 編譯測試,"Hello world" 與 "world" 兩個字符串扶认,只存儲前面一個侨拦。取的時候只需要給前面和中間的地址,如果是整形辐宾、浮點型會有多份拷貝狱从,但這些數(shù)寫在指令中。占的只是代碼段而已叠纹,大量用宏會導(dǎo)致二進(jìn)制文件變大季研。
- 變量:共享一塊內(nèi)存空間,就算項目中N處用到誉察,也不會分配N塊內(nèi)存空間与涡,可以被修改,在編譯階段會執(zhí)行類型檢查冒窍。
- 常量:共享一塊內(nèi)存空間递沪,就算項目中N處用到,也不會分配N塊內(nèi)存空間综液,可以根據(jù)const修飾的位置設(shè)定能否修改款慨,在編譯階段會執(zhí)行類型檢查。
2.2 常量區(qū)分
全局常量: 不管你定義在任何文件夾谬莹,外部都能訪問檩奠。
const NSString *HSCoder = @"漢斯哈哈哈";
局部常量:用static修飾后桩了,不能提供外界訪問
static const NSString *HSCoder = @"漢斯哈哈哈";
ps :static經(jīng)常用到.m文件中,如果是.h文件定義的全局變量埠戳,在別的文件中井誉,引入這個頭文件,也是可以訪問這個變量的整胃。
2.3const修飾位置不同颗圣,代表什么?
1.const NSString *HSCoder = @"漢斯哈哈哈";
"*HSCoder"不能被修改屁使, "HSCoder"能被修改
2.NSString const *HSCoder = @"漢斯哈哈哈";
"*HSCoder"不能被修改在岂, "HSCoder"能被修改
3.NSString * const HSCoder = @"漢斯哈哈哈";
"HSCoder"不能被修改,"*HSCoder"能被修改
結(jié)論:const右邊的總不能被修改
驗證:
所以一般我們定義一個常量又不想被修改應(yīng)該這樣:
NSString * const HSCoder = @"漢斯哈哈哈";
一般項目中蛮寂,定義全局常量蔽午,會寫在獨立文件中
訪問:
導(dǎo)入頭文件
訪問常量