3.1 OC特性之 內(nèi)存五大區(qū)域
此篇為針對Objective-c語言入門的基礎(chǔ)知識,為了能讓大家更清楚的理解,此整理中編寫了許多的代碼案例和部分截圖,如有錯誤之處,望指正,愿與您相互交流學(xué)習(xí),共同進(jìn)步!---"會飛的猴子_阿新" (同時(shí)還要向刀哥致敬)
本篇目標(biāo)是: 理解內(nèi)存五大區(qū)域及各自的職責(zé)
00.簡述
程序要想執(zhí)行,第一步就需要 被加載到內(nèi)存中
-
棧區(qū)
: 局部變量和方法實(shí)參 -
堆區(qū)
:OC中使用new方法創(chuàng)建的對象,被創(chuàng)建對象的所有成員變量保存在堆區(qū)中. -
BSS段(也叫靜態(tài)區(qū))
:
教科書
:未被初始化的全局變量和靜態(tài)變量.
Xcode8中
: 全局變量和靜態(tài)變量,不管有沒有被初始化,都存放在BSS段中.驗(yàn)證見本篇中的案例. -
常量區(qū)(也叫數(shù)據(jù)段)
:
教科書: 存儲已經(jīng)初始化的全局變量,靜態(tài)變量,常量.
xcode8: 存儲常量 -
代碼段
: 程序的代碼.
內(nèi)存五大區(qū)域01
01. 分配和釋放(面試常被問到)
-
棧區(qū)
(stack [st?k]): 由編譯器自動分配釋放
- 局部變量是保存在棧區(qū)的
- 方法調(diào)用的實(shí)參也是保存在棧區(qū)的
-
堆區(qū)
(heap [hi?p]): 由程序員分配釋放,若程序員不釋放,會出現(xiàn)內(nèi)存泄漏
- 賦值語句右側(cè) 使用 new 方法創(chuàng)建的對象
- 被創(chuàng)建對象的所有 成員變量
BSS 段
: 程序結(jié)束后由系統(tǒng)釋放數(shù)據(jù)段
: 程序結(jié)束后由系統(tǒng)釋放-
代碼段
:程序結(jié)束后由系統(tǒng)釋放程序 編譯鏈接 后的二進(jìn)制可執(zhí)行代碼
02.棧區(qū)和堆區(qū)
int main(int argc, const char * argv[]) {
// 局部變量是保存在棧區(qū)的
// 棧區(qū)變量出了作用域之后,就會被銷毀
NSInteger i = 10;
NSLog(@"%zd", i);
// 局部變量是保存在棧區(qū)的
// 賦值語句右側(cè),使用 new 方法創(chuàng)建的對象是保存在堆區(qū)的
// xinge 變量中,記錄的是堆區(qū)的地址
// 在 OC 中,有一個內(nèi)存管理機(jī)制,叫做 `ARC`,可以自動管理 OC 代碼創(chuàng)建對象的生命周期
// 因此,在開發(fā) OC 程序的時(shí)候,程序員通常不需要考慮內(nèi)存釋放的工作
LJXPerson *xinge = [LJXPerson new];
NSLog(@"%@", xinge);
return 0;
}
10 <LJXPerson: 0x100404b70>
2.1 棧區(qū)
棧區(qū) (stack [st?k]) : 由編譯器自動分配釋放
2.1.1 棧區(qū)中的保存(棧區(qū)的職責(zé)/存儲的內(nèi)容)
局部變量
-
方法實(shí)參
(eg:在main函數(shù)中,調(diào)用方法,方法中的實(shí)參)
2.1.2 棧區(qū)的特點(diǎn)
-
存儲空間有限
. iphone的棧區(qū)大小只有512k
(默認(rèn)) ,非常有限 -
連續(xù)性
. 棧區(qū)的地址是連續(xù)的
部分代碼:
#import <Foundation/Foundation.h>
#import "LJXPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSInteger i = 10;
NSLog(@"i 的棧區(qū)地址是 %p", &i);
NSInteger j = 10;
NSLog(@"j 的棧區(qū)地址是 %p", &j);
// xiaoming 變量中,存儲的是 LJXPerson
對象的內(nèi)存地址(堆區(qū)的內(nèi)存地址)
LJXPerson *xinge = [LJXPerson new];
NSLog(@"xinge 的棧區(qū)地址是 %p", &xinge);
double height = 10;
NSLog(@"height 的棧區(qū)地址是 %p", &height);
.......
i 的棧區(qū)地址是 0x7fff5fbff768 j 的棧區(qū)地址是 0x7fff5fbff760 xinge 的棧區(qū)地址是 0x7fff5fbff758 height 的棧區(qū)地址是 0x7fff5fbff750
-
地址分配從大到小
. 棧區(qū)地址按照分配的順序,由大到小順序排列 -
訪問速度快
. -
系統(tǒng)管理
. (棧區(qū)的內(nèi)存由系統(tǒng)管理)
2.1.3 其他
如果在程序中調(diào)用方法,會開啟一個 " 棧幀 ".(這個棧幀可以理解為也是一塊連續(xù)的區(qū)域)
棧幀的地址與之前的局部變量的地址不是連續(xù)的棧幀中記錄實(shí)參地址,
以及方法內(nèi)部的局部變量方法執(zhí)行完畢后,棧幀銷毀(彈棧)
<<<這樣每次執(zhí)行完畢后,都彈棧釋放內(nèi)存,這樣就會始終保證棧區(qū)占用的內(nèi)存不會特別大>>
so-課外話->我們在開發(fā)的時(shí)候,如果每個方法都寫的很短,同時(shí)每個方法聲明的變量都很少.
這樣做一定會節(jié)約內(nèi)存


見圖知意

添加 "在一個函數(shù)/方法中"
即在一個函數(shù)/方法中最多可定義65536,--->因?yàn)?每次"執(zhí)行完"一個"方法之后就會彈棧釋放內(nèi)存

棧的概念: 后進(jìn)先出/ 先進(jìn)后出
見圖知意:
NSLog(@"NSInteger 變量占用的字節(jié)數(shù) %tu", sizeof(NSInteger));
NSLog(@"double 變量占用的字節(jié)數(shù) %tu", sizeof(double));
NSLog(@"xinge 變量占用的字節(jié)數(shù) %tu", sizeof(xinge));
// 提示:棧區(qū)中,只適合存儲非常小的數(shù)據(jù)
NSLog(@"在 iPhone 中,最多可以在一個函數(shù)/方法中定義 %zd 個局部變量", 512 * 1024 / 8);
// 測試方法調(diào)用時(shí),實(shí)參在棧區(qū)的存儲情況
[xinge sumWithNum1:i andNum2:j];

總結(jié): 調(diào)用方法時(shí)棧區(qū)的工作原理
* 開啟棧幀
* 保存實(shí)參
* 保存局部變量
* 方法完成后彈棧,銷毀棧幀,釋放空間
2.2 堆區(qū)
堆區(qū) (heap [hi?p]): 由程序員分配釋放,若程序員不釋放,會出現(xiàn)內(nèi)存泄漏

2.2.1 堆區(qū)中保存:
- 使用 new 方法創(chuàng)建的對象保存在堆區(qū)
- 被創(chuàng)建對象的所有成員變量保存在堆區(qū)中
堆區(qū)的職責(zé)是"解決棧區(qū)空間有限的問題"
* OC 使用`new`方法創(chuàng)建的對象
--->{由于 **ARC 管理機(jī)制**,OC 程序員通常不需要考慮對象的釋放.}
(在 OC 中,有一個內(nèi)存管理機(jī)制,叫做 ARC(自動引用計(jì)數(shù))
可以自動管理 OC 代碼創(chuàng)建對象的生命周期)
* C 語言使用 malloc、calloc、realloc 函數(shù)分配的空間,需要使用 free 函數(shù)釋放
顧: 在開發(fā) OC 程序的時(shí)候,程序員通常不需要考慮內(nèi)存釋放的工作
但是:如果在 OC 的代碼中,如果使用到 C 語言分配空間的函數(shù),則需要考慮釋放內(nèi)存
</br>
1. 堆區(qū)的大小由系統(tǒng)決定,包括:系統(tǒng)內(nèi)存/磁盤交換空間...
2. 系統(tǒng)使用`鏈表`來管理堆區(qū)中的內(nèi)存分配情況
3. {**程序員只需要負(fù)責(zé)堆區(qū)中內(nèi)存的分配和釋放工作**}
2.2.2能夠說出堆區(qū)的特點(diǎn)
所有程序共享
存儲大數(shù)據(jù)
-
程序員管理
: 堆區(qū)的內(nèi)存需要程序員管理 -
不連續(xù)
: 堆區(qū)的地址是不連續(xù)的 -
速度沒有棧區(qū)快
: 堆區(qū)的訪問速度沒有棧區(qū)快,因?yàn)槲覀円L問堆區(qū)中創(chuàng)建對象的屬性,
必須先需要通過變量找到棧區(qū)的地址,再通過地址定位到到堆區(qū)中的某一個位置,
只有找個這個位置之后,我們才可以訪問到存儲到這個對象中屬性對應(yīng)的數(shù)值.由于有了
這個地址尋找的過程,所有速度沒有棧區(qū)的快.
0.3 全局變量、靜態(tài)變量和常量
3.1 全局變量/靜態(tài)變量/常量保存的內(nèi)存區(qū)域
開發(fā)要讓 變化控制在有限的范圍內(nèi)
3.1.1 教科書中 全局變量 和 靜態(tài)變量 的存儲區(qū)域
-
有初始值的
全局變量 和 靜態(tài)變量 保存在 數(shù)據(jù)段(常量區(qū)) -
沒有初始值的
全局變量 和 靜態(tài)變量 保存在 BSS 段(靜態(tài)區(qū))
當(dāng)給 全局變量 或 靜態(tài)變量 設(shè)置初始值后,會被移動到 數(shù)據(jù)段(常量區(qū))
3.1.2 Xcode 8 中 全局變量 和 靜態(tài)變量 的存儲區(qū)域
無論是否設(shè)置初始值,
全局變量 和 靜態(tài)變量
都保存在 BSS 段(靜態(tài)區(qū))常量
存儲在 數(shù)據(jù)段(常量區(qū))
注意:(全局/靜態(tài)變量,常量)教科書與xcode8驗(yàn)證有點(diǎn)差別
(1) xcode8 "全局變量"實(shí)際存儲區(qū)域案例驗(yàn)證:
案例代碼:
#import <Foundation/Foundation.h>
NSInteger num1 = 10; //定義第一個全局變量 并且初始化
NSInteger num2; //定義第二個全局變量 沒有初始化
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 提示:在 Xcode 8 中,全局變量無論是否初始化,地址保持不變
NSLog(@"第1個全局變量的地址%p", &num1); //第1個全局變量的地址0x100001188
NSLog(@"第2個全局變量的地址%p", &num2); //第2個全局變量的地址0x100001190
num2=100;
NSLog(@"第2個全局變量的地址%p(初始化后的)", &num2);
//第2個全局變量的地址0x100001190(初始化后的)
}
return 0;
}
第1個全局變量的地址0x100001188 第2個全局變量的地址0x100001190 第2個全局變量的地址0x100001190(初始化后的) Program ended with exit code: 0
可見1:地址從小到大是連續(xù)的

可見2:
在 Xcode 8 中,全局變量無論是否初始化,地址保持不變

(2)xcode8 "靜態(tài)變量"實(shí)際存儲區(qū)域案例驗(yàn)證:
#import <Foundation/Foundation.h>
static NSInteger num1 = 10; //定義第一個全局變量 并且初始化
static NSInteger num2; //定義第二個全局變量 沒有初始化
static NSInteger sNum1 = 10; //定義第一個靜態(tài)的全局變量 并且初始化
static NSInteger sNum2; //定義第二個靜態(tài)的全局變量 沒有初始化
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 提示:在 Xcode 8 中,全局變量無論是否初始化,地址保持不變
NSLog(@"第1個全局變量的地址%p", &num1); //第1個全局變量的地址0x1000011f8
NSLog(@"第2個全局變量的地址%p", &num2); //第2個全局變量的地址0x100001208
num2=100;
NSLog(@"第2個全局變量的地址%p(初始化后的)", &num2);//第2個全局變量的地址0x100001208(初始化后的)
NSLog(@"##############################################");
NSLog(@"第1個靜態(tài)的全局變量的地址%p", &sNum1); //第1個靜態(tài)的全局變量的地址0x100001200
NSLog(@"第2個靜態(tài)的全局變量的地址%p", &sNum2); //第2個靜態(tài)的全局變量的地址0x100001210
sNum2=100;
NSLog(@"第2個靜態(tài)的全局變量的地址%p(初始化后的)", &sNum2);//第2個靜態(tài)的全局變量的地址0x100001210(初始化后的)
}
return 0;
}
第1個全局變量的地址0x1000011f8 第2個全局變量的地址0x100001208 第2個全局變量的地址0x100001208(初始化后的) ################################ 第1個靜態(tài)的全局變量的地址0x100001200 第2個靜態(tài)的全局變量的地址0x100001210 第2個靜態(tài)的全局變量的地址0x100001210(初始化后的) Program ended with exit code: 0
可見1. 靜態(tài)變量的初始化前后地址也是不變的

可見2. 靜態(tài)變量和全局變量是交叉保存的

so-->面試注意:
---->在xcode 8中, 靜態(tài)變量和全局變量保存在同一個區(qū)域
---->在xcode 8中, 靜態(tài)變量和全局變量都保存在BSS段中.
跟我們教課書上是有區(qū)別的,
so-->當(dāng)面試中遇到這個問題時(shí),我們可以這樣回答:
在教課書中:BSS段存放的是未被初始化的全局變量和靜態(tài)變量,
但是我們通過xcode8 驗(yàn)證后,他們不管是全局變量
還是靜態(tài)變量都存儲在BSS段中.
(3)xcode8 "常量"實(shí)際存儲區(qū)域案例驗(yàn)證:
#import <Foundation/Foundation.h>
NSInteger num1 = 10;
NSInteger num2;
static NSInteger sNum1 = 10;
static NSInteger sNum2;
const NSInteger cNum = 10000;//定義一個常量
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"第1個全局變量的地址%p", &num1);
NSLog(@"第2個全局變量的地址%p", &num2);
num2=100;
NSLog(@"第2個全局變量的地址%p(初始化后的)", &num2);
NSLog(@"##############################################");
NSLog(@"第1個靜態(tài)的全局變量的地址%p", &sNum1);
NSLog(@"第2個靜態(tài)的全局變量的地址%p", &sNum2);
sNum2=100;
NSLog(@"第2個靜態(tài)的全局變量的地址%p(初始化后的)", &sNum2);
//第2個靜態(tài)的全局變量的地址0x100001230(初始化后的)
NSLog(@"##############################################");
NSLog(@"第1個常量的地址%p", &cNum);
//第1個常量的地址0x100000e88
}
return 0;
}
第1個全局變量的地址0x100001218 第2個全局變量的地址0x100001228 第2個全局變量的地址0x100001228(初始化后的) ################# 第1個靜態(tài)的全局變量的地址0x100001220 第2個靜態(tài)的全局變量的地址0x100001230 第2個靜態(tài)的全局變量的地址0x100001230(初始化后的) ################## 第1個常量的地址0x100000e88 Program ended with exit code: 0

常量存儲演示:面試萬一遇到,可演示一下,解釋與科普書上的不同
可見: 全局變量和靜態(tài)變量 跟常量放在不同的區(qū)域中,常量存放在常量區(qū)(或叫數(shù)據(jù)段).
* 教科書:數(shù)據(jù)段(常量區(qū))用來存儲已經(jīng)初始化的全局變量,靜態(tài)變量,常量.
* xcode8:中數(shù)據(jù)段(常量區(qū))存放的是常量.
3.2 BSS段(靜態(tài)區(qū))
存儲的內(nèi)容: (前面已經(jīng)案例驗(yàn)證過了):
-
教科書: 存儲
未初始化
的 全局變量 和 靜態(tài)變量 -
Xcode 8驗(yàn)證: 不管有沒有初始化
均存儲 全局變量 和 靜態(tài)變量
.
3.3 數(shù)據(jù)段(常量區(qū))
存儲的內(nèi)容: (前面已經(jīng)案例驗(yàn)證過了):
教科書 :存儲
已經(jīng)初始化
的 全局變量、靜態(tài)變量、常量Xcode 8驗(yàn)證 :不管有沒有初始化
均存儲 常量
.
3.4 全局變量與全局靜態(tài)變量的區(qū)別
- 若程序只有一個文件,
全局變量
與全局靜態(tài)變量
沒有區(qū)別 (程序中只有一個文件,幾乎是不存在的,因?yàn)橥ǔR粋€類就對應(yīng)2個文件.) - 若程序由多個文件構(gòu)成時(shí),
全局變量
與全局靜態(tài)變量
不同:
- 全局靜態(tài)變量 通常在定義該變量的文件內(nèi)部使用
注意:
注意:
1.為什么OC中幾乎不使用 全局變量?
1. 因?yàn)槌绦虻娜魏我粋€位置都可以對 全局變量 進(jìn)行修改
2. 一旦程序出現(xiàn)由全局變量產(chǎn)生的錯誤,不好排查錯誤
3. 使用全局變量不是一個好習(xí)慣
[我的理解:因?yàn)楫?dāng)我們開發(fā)時(shí)文件有很多,但是又不能把全局變量定義到頭文件中
(如果定義在頭文件中,當(dāng)別的類引入該頭文件后,相當(dāng)于在兩個類中都低定義了該全局變量,
會出現(xiàn)重復(fù)定義),所有幾乎不使用.]
2.不能把 全局變量 定義在頭文件中
(我的理解:如果定義在頭文件中,當(dāng)別的類引入該頭文件后,相當(dāng)于在兩個類中都定義了該全局變量,會出現(xiàn)重復(fù)定義)
3.在程序開發(fā)時(shí),全局變量 和 靜態(tài)變量 都不能重名
(我的理解:當(dāng)跟別的變量重名時(shí),會被重名的變量修改.)
4.靜態(tài)變量 的修改通常只是在 定義當(dāng)前靜態(tài)變量的文件 內(nèi)部進(jìn)行
比如:我在LJXPerson.h中聲明一個靜態(tài)變量,修改通常只是在定義當(dāng)前靜態(tài)變量的文件(LJXPerson.h和LJXPerson.m)內(nèi)部進(jìn)行,如果外部比如main.m即使修改,但是由于地址不同,也不會影響到定義靜態(tài)變量文件 內(nèi)部的數(shù)值變化,因?yàn)楫?dāng)main.m引入LJXPerson.h頭文件后,引入的靜態(tài)變量跟LJXPerson.h中的靜態(tài)變量存放的值的地址不是一個地址.如果不明白見下面的案例(LJXPerson.h中定義的靜態(tài)變量static NSInteger num1 = 99;num1地址為0x100001190,而main.m中引入LJXPerson.h后,相當(dāng)于static NSInteger num1 = 99;也拷貝進(jìn)去了,但是這個num1中的地址是0x100001198,并不是0x100001190, 當(dāng)我在main.m中給num1重新賦值777777時(shí),0x100001198對應(yīng)的num1被修改為777777,而LJXPerson.h中的num1還是99,當(dāng)我在main.m中創(chuàng)建兩個對象,對在LJXPerson.h聲明的方法和LJXPerson.m實(shí)現(xiàn)的方法,進(jìn)行調(diào)用時(shí),調(diào)用的是內(nèi)部的文件,顧內(nèi)部文件中的num1即對象的0x100001198地址的變量會被修改.),此處提醒在以后開發(fā)程序是,全局的靜態(tài)變量要放在.m中,這樣才會把變量范圍控制范圍變小,越容易控制,只要靜態(tài)變量出現(xiàn)問題才會越容易排查.
外部即使修改,但是由于地址不同,也不會影響到
定義靜態(tài)變量文件
內(nèi)部的數(shù)值變化
一旦程序因?yàn)?靜態(tài)變量 產(chǎn)生錯誤,只需要在當(dāng)前文件中,檢查一下修改 靜態(tài)變量 的代碼即可,排查錯誤難度相對降低
提示:在程序開發(fā)時(shí),如果要使用全局的變量,應(yīng)該在 .m 中定義一個全局的靜態(tài)變量,而不要把該變量暴露在頭文件中-->因?yàn)樽兞糠秶叫≡娇煽?
------------------LJXPerson.h---------------------
#import <Foundation/Foundation.h>
//
static NSInteger num1 = 99; //定義一個全局靜態(tài)變量
@interface LJXPerson : NSObject
/**
測試方法
*/
- (void)test;
@end
------------------LJXPerson.m---------------------
**LJXPerson.m**
#import "LJXPerson.h"
@implementation LJXPerson
- (void)test{
num1--;
NSLog(@"%p %zd",&num1,num1);
}
@end
------------------main.m-------------------------
#import <Foundation/Foundation.h>
#import "LJXPerson.h"
int main(int argc, const char * argv[]) {
//測試全局的靜態(tài)變量
NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
num1=777777;
NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
LJXPerson *xinge=[LJXPerson new];
[xinge test];
LJXPerson *xiaoming=[LJXPerson new];
[xiaoming test];
NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
return 0;
}

在LJXPerson中定義的全局靜態(tài)變量的地址0x100001190,99 在LJXPerson中定義的全局靜態(tài)變量的地址0x100001190,777777 0x100001198 98 0x100001198 97 在LJXPerson中定義的全局靜態(tài)變量的地址0x100001190,777777
通過:上面的輸出結(jié)果截圖也對剛才上面的闡述得到了驗(yàn)證,以下代碼第6,7,8,13,14行是調(diào)用的外部的,即修改的是地址0x100001190對應(yīng)的變量的值,只有在對象調(diào)用方法時(shí)是調(diào)用的內(nèi)部的靜態(tài)變量.
//測試全局的靜態(tài)變量
6. NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
7. num1=777777;
8. NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
9. LJXPerson *xinge=[LJXPerson new];
10. [xinge test];
11. LJXPerson *xiaoming=[LJXPerson new];
12. [xiaoming test];
13. NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
14. return 0;
}
main函數(shù)中:注釋掉下面的代碼
// NSLog(@"在LJXPerson中定義的全局靜態(tài)變量的地址%p,%zd", &num1,num1);
// num1=777777;

在LJXPerson中定義的全局靜態(tài)變量的地址0x100001190,99 0x100001198 98 0x100001198 97 在LJXPerson中定義的全局靜態(tài)變量的地址0x100001190,99
3.5 靜態(tài)變量的正確用法
- 先定義局部靜態(tài)變量
- static 關(guān)鍵字定義的變量在程序執(zhí)行中
只會被執(zhí)行一次
- 在 BSS 段為變量分配空間
- 并且設(shè)置初始值,如果沒有指定初始值,會使用 0 作為初始值
即: static 關(guān)鍵字的作用
- 在 BSS 段為 靜態(tài)變量 分配空間
- 為 靜態(tài)變量 設(shè)置初始值,如果沒有指定初始值,會使用 0 來作為初始值
- static 關(guān)鍵字定義靜態(tài)變量的代碼,只會被執(zhí)行一次
------------------聲明---------------------
#import <Foundation/Foundation.h>
@interface LJXPerson : NSObject
/**
測試方法
*/
- (void)test;
@end
-------------------實(shí)現(xiàn)--------------------
#import "LJXPerson.h"
@implementation LJXPerson
- (void)test{
static NSInteger num1 = 99; //定義局部靜態(tài)變量
num1--;
NSLog(@"%p %zd",&num1,num1);
}
@end
-------------------主程序--------------------
#import <Foundation/Foundation.h>
#import "LJXPerson.h"
int main(int argc, const char * argv[]) {
LJXPerson *xinge=[LJXPerson new];
[xinge test];
LJXPerson *xiaoming=[LJXPerson new];
[xiaoming test];
return 0;
}
2030-03-31 15:12:48.504 demo[5399:535229] 0x100001170 98 2030-03-31 15:12:48.505 demo[5399:535229] 0x100001170 97 Program ended with exit code: 0
3. 如果當(dāng)前類文件中,有 多個方法
使用到該靜態(tài)變量,再將該靜態(tài)變量修改成全局的
------------------聲明---------------------
#import <Foundation/Foundation.h>
@interface LJXPerson : NSObject
/**
測試方法
*/
- (void)test;
- (void)demo;
@end
-------------------實(shí)現(xiàn)--------------------
#import "LJXPerson.h"
//當(dāng)前類文件中,有 `多個方法` 使用到該靜態(tài)變量,放到全局中
static NSInteger num1 = 99;
@implementation LJXPerson
- (void)test{
num1--;
NSLog(@"%p %zd",&num1,num1);
}
- (void)demo{
num1++;
NSLog(@"%p %zd",&num1,num1);
}
@end
-------------------主程序--------------------
#import <Foundation/Foundation.h>
#import "LJXPerson.h"
int main(int argc, const char * argv[]) {
LJXPerson *xinge=[LJXPerson new];
[xinge test];
[xinge demo];
LJXPerson *xiaoming=[LJXPerson new];
[xiaoming test];
[xiaoming demo];
return 0;
}
0x1000011b0 98 0x1000011b0 99 0x1000011b0 98 0x1000011b0 99
3.6 常量
作用:定義一個
固定不變
的值,全局統(tǒng)一使用,eg:公司網(wǎng)址,電話等
const 關(guān)鍵字保證其后修飾的常量的值不允許被修改
常量的工作原理: 在 程序被加載到內(nèi)存時(shí),就會為常量分配空間并且設(shè)置初始值 在 數(shù)據(jù)段 為常量分配空間 如果沒有指定初始值,會使用 0 作為初始值
不能把 常量 定義在頭文件中
(跟全局變量不能定義在頭文件中一樣,會導(dǎo)致重復(fù)定義)應(yīng)該在 .m 中定義常量
比如我們在LJXPerson.m中定義了一個常量,const NSInteger cNumber =100;這時(shí),這個常量只能在LJXPerson.m中使用,怎么才能也能在main.m中使用呢,
那就需要下面介紹的第4個知識點(diǎn)了.
在LJXPerson.h
頭文件中做聲明.extern
const NSInteger cNumber;
特別注意,不能再給設(shè)置初始值了,
它已經(jīng)在其他文件中(eg:在LJXPerson.m中設(shè)置值為100了),我們要清楚extern的作用,表示該常量的數(shù)值,是在其他文件中設(shè)置的,外部可以直接使用此常量)
總之:
eg:在.m中: const NSInteger cNumber =100;
eg:在.h中: extern const NSInteger cNumber;
- 在 .h 中使用 extern 關(guān)鍵字聲明該 常量 在其他文件設(shè)置值
extern的作用: extern關(guān)鍵字,表示該常量的數(shù)值,是在其他文件中設(shè)置的,
外部可以直接使用此常量.
注意:在一個項(xiàng)目中,常量不能重名,因此在定義常量時(shí),應(yīng)該盡量長,有前綴,以保證不會出現(xiàn)重名的情況
本篇主要學(xué)習(xí)目標(biāo)回顧:
內(nèi)存五大區(qū)域
程序要執(zhí)行,首先需要被加載到內(nèi)存
1.1 記憶內(nèi)存五大區(qū)域的名稱
能夠說出內(nèi)存五大區(qū)域的名稱
棧區(qū)
堆區(qū)
BSS段(靜態(tài)區(qū))
數(shù)據(jù)段(常量區(qū))
代碼段
1.2 記憶棧區(qū)/堆區(qū)的職責(zé)及特點(diǎn)
能夠說出棧區(qū)職責(zé)(存儲的內(nèi)容)
- 局部變量
- 方法實(shí)參
能夠說出棧區(qū)的特點(diǎn)
512K
連續(xù)
從大到小
速度快
系統(tǒng)管理
能夠說出調(diào)用方法時(shí)棧區(qū)的工作原理
開啟棧幀
保存實(shí)參
保存局部變量
方法完成后彈棧,銷毀棧幀,釋放空間
能夠說出堆區(qū)的職責(zé)(存儲的內(nèi)容)
- OC 使用 new 方法創(chuàng)建的對象
由于 ARC 管理機(jī)制,OC 程序員通常不需要考慮對象的釋放 - C 語言使用 malloc 函數(shù)分配的空間,需要使用 free 函數(shù)釋放
能夠說出堆區(qū)的特點(diǎn)
所有程序共享
存儲大數(shù)據(jù)
程序員管理
不連續(xù)
速度沒有棧區(qū)快
1.3 記憶全局變量/靜態(tài)變量/常量保存的內(nèi)存區(qū)域
開發(fā)要讓 變化控制在有限的范圍 內(nèi)
能夠說出教科書中 全局變量 和 靜態(tài)變量 的存儲區(qū)域
- 有初始值 的 全局變量 和 靜態(tài)變量 保存在 數(shù)據(jù)段(常量區(qū))
- 沒有初始值 的 全局變量 和 靜態(tài)變量 保存在 BSS 段(靜態(tài)區(qū))
當(dāng)給 全局變量 或 靜態(tài)變量 設(shè)置初始值后,會被移動到 數(shù)據(jù)段(常量區(qū))
能夠說出 Xcode 8 中 全局變量 和 靜態(tài)變量 的存儲區(qū)域
- 無論是否設(shè)置初始值,全局變量 和 靜態(tài)變量 都保存在 BSS 段(靜態(tài)區(qū))
能夠說出 常量 的存儲區(qū)域
常量 存儲在 數(shù)據(jù)段(常量區(qū))
能夠說出為什么幾乎不使用 全局變量
開發(fā)要讓 變化控制在有限的范圍 內(nèi)
不能把 全局變量 定義在頭文件中,否則會出現(xiàn)重復(fù)定義
1.4 記憶靜態(tài)變量/常量的用法
能夠說出 static 關(guān)鍵字的作用
- 在 BSS 段為 靜態(tài)變量 分配空間
- 為 靜態(tài)變量 設(shè)置初始值,如果沒有指定初始值,會使用 0 來作為初始值
- static 關(guān)鍵字定義靜態(tài)變量的代碼,只會被執(zhí)行一次!
能夠說出靜態(tài)變量的正確用法
- 如果只有一個方法使用,將 靜態(tài)變量 定義在方法內(nèi)部
- 如果有多個方法使用,將 靜態(tài)變量 定義在 .m 中
- 不要把靜態(tài)變量定義在頭文件中
能夠說出常量的作用
定義一個固定不變的值,全局統(tǒng)一使用,例如公司的網(wǎng)址/電話等...
在 iOS 開發(fā)中,通常僅僅在定義 通知字符串 時(shí)才會使用到常量
能夠說出常量的正確用法
在 .m 中定義常量并且設(shè)置初始值
const NSInteger cNum = 99;
在 .h 中使用 extern 關(guān)鍵字聲明常量在其他位置定義并且已經(jīng)賦值,外部可以直接使用
extern const NSInteger cNum;
常量名應(yīng)該盡量的長以避免出現(xiàn)重名
結(jié)束語
ok! 以上關(guān)于內(nèi)存五大區(qū)域基礎(chǔ)知識對您起到學(xué)習(xí)作用了嗎?如果有錯誤之處望指正!謝謝!相關(guān)學(xué)習(xí)共同進(jìn)步!真心希望其中某個知識點(diǎn)能幫到您!