3.1 OC特性之 內(nèi)存五大區(qū)域

3.1 OC特性之 內(nèi)存五大區(qū)域

此篇為針對Objective-c語言入門的基礎(chǔ)知識,為了能讓大家更清楚的理解,此整理中編寫了許多的代碼案例和部分截圖,如有錯誤之處,望指正,愿與您相互交流學(xué)習(xí),共同進(jìn)步!---"會飛的猴子_阿新" (同時(shí)還要向刀哥致敬)

本篇目標(biāo)是: 理解內(nèi)存五大區(qū)域及各自的職責(zé)

00.簡述

程序要想執(zhí)行,第一步就需要 被加載到內(nèi)存中

  1. 棧區(qū): 局部變量和方法實(shí)參
  2. 堆區(qū):OC中使用new方法創(chuàng)建的對象,被創(chuàng)建對象的所有成員變量保存在堆區(qū)中.
  3. BSS段(也叫靜態(tài)區(qū)):
    教科書:未被初始化的全局變量和靜態(tài)變量.
    Xcode8中: 全局變量和靜態(tài)變量,不管有沒有被初始化,都存放在BSS段中.驗(yàn)證見本篇中的案例.
  4. 常量區(qū)(也叫數(shù)據(jù)段):
    教科書: 存儲已經(jīng)初始化的全局變量,靜態(tài)變量,常量.
    xcode8: 存儲常量
  5. 代碼段: 程序的代碼.

    內(nèi)存五大區(qū)域01
    內(nèi)存五大區(qū)域01

01. 分配和釋放(面試常被問到)

  • 棧區(qū) (stack [st?k]): 由編譯器自動分配釋放
  1. 局部變量是保存在棧區(qū)的
  2. 方法調(diào)用的實(shí)參也是保存在棧區(qū)的
  • 堆區(qū) (heap [hi?p]): 由程序員分配釋放,若程序員不釋放,會出現(xiàn)內(nèi)存泄漏
  1. 賦值語句右側(cè) 使用 new 方法創(chuàng)建的對象
  2. 被創(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)存
-w440
-w440

-w440
-w440

見圖知意


添加 "在一個函數(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];        
-w480
-w480
總結(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)

  1. 所有程序共享
  2. 存儲大數(shù)據(jù)
  3. 程序員管理: 堆區(qū)的內(nèi)存需要程序員管理
  4. 不連續(xù): 堆區(qū)的地址是不連續(xù)的
  5. 速度沒有棧區(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ū)域

  1. 有初始值的 全局變量 和 靜態(tài)變量 保存在 數(shù)據(jù)段(常量區(qū))
  2. 沒有初始值的 全局變量 和 靜態(tài)變量 保存在 BSS 段(靜態(tài)區(qū))
    當(dāng)給 全局變量 或 靜態(tài)變量 設(shè)置初始值后,會被移動到 數(shù)據(jù)段(常量區(qū))

3.1.2 Xcode 8 中 全局變量 和 靜態(tài)變量 的存儲區(qū)域

  1. 無論是否設(shè)置初始值全局變量 和 靜態(tài)變量 都保存在 BSS 段(靜態(tài)區(qū))

  2. 常量 存儲在 數(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)證過了):

  1. 教科書: 存儲未初始化的 全局變量 和 靜態(tài)變量
  2. Xcode 8驗(yàn)證: 不管有沒有初始化均存儲 全局變量 和 靜態(tài)變量.

3.3 數(shù)據(jù)段(常量區(qū))

存儲的內(nèi)容: (前面已經(jīng)案例驗(yàn)證過了):

  1. 教科書 :存儲已經(jīng)初始化的 全局變量、靜態(tài)變量、常量

  2. Xcode 8驗(yàn)證 :不管有沒有初始化均存儲 常量.

3.4 全局變量與全局靜態(tài)變量的區(qū)別

  1. 若程序只有一個文件全局變量全局靜態(tài)變量沒有區(qū)別 (程序中只有一個文件,幾乎是不存在的,因?yàn)橥ǔR粋€類就對應(yīng)2個文件.)
  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)變量的正確用法

  1. 先定義局部靜態(tài)變量
  2. static 關(guān)鍵字定義的變量在程序執(zhí)行中 只會被執(zhí)行一次
  • 在 BSS 段為變量分配空間
  • 并且設(shè)置初始值,如果沒有指定初始值,會使用 0 作為初始值

即: static 關(guān)鍵字的作用

  1. 在 BSS 段為 靜態(tài)變量 分配空間
  2. 為 靜態(tài)變量 設(shè)置初始值,如果沒有指定初始值,會使用 0 來作為初始值
  3. 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)址,電話等

  1. const 關(guān)鍵字保證其后修飾的常量的值不允許被修改
    常量的工作原理: 在 程序被加載到內(nèi)存時(shí),就會為常量分配空間并且設(shè)置初始值 在 數(shù)據(jù)段 為常量分配空間 如果沒有指定初始值,會使用 0 作為初始值

  2. 不能把 常量 定義在頭文件中
    (跟全局變量不能定義在頭文件中一樣,會導(dǎo)致重復(fù)定義)

  3. 應(yīng)該在 .m 中定義常量
    比如我們在LJXPerson.m中定義了一個常量,const NSInteger cNumber =100;這時(shí),這個常量只能在LJXPerson.m中使用,怎么才能也能在main.m中使用呢,
    那就需要下面介紹的第4個知識點(diǎn)了.
    在LJXPerson.h頭文件中做聲明.externconst 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;
  1. 在 .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)鍵字的作用

  1. 在 BSS 段為 靜態(tài)變量 分配空間
  2. 為 靜態(tài)變量 設(shè)置初始值,如果沒有指定初始值,會使用 0 來作為初始值
  3. 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)能幫到您!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容