在內存剖析對象

對象在內存中的存儲

  • 棧、堆、BSS、數據段、代碼段是什麼?

    • 棧(stack):又稱作堆棧,用來存儲程序的局部變量(但不包括static聲明的變量,static修飾的數據存放於數據段中)。除此之外,在函數被調用時,棧用來傳遞參數和返回值。

    • 堆(heap):用於存儲程序運行中被動態分配的內存段,它的大小並不固定,可動態的擴張和縮減。操作函數(malloc/free)

    • BSS段(bss segment):通常用來存儲程序中未被初始化的全局變量和靜態變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段輸入靜態內存分配

    • 數據段(data segment):通常用來存儲程序中已被初始化的全局變量和靜態變量和字符串的一塊內存區域

    • 代碼段(code segment):通常是指用來存儲程序可執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀,某些架構也允許代碼段爲可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量。

內存

  • 搞清楚上面的概念再來研究下對象在內存中如何存儲?
Person *p1 = [Person new]

看這行代碼,先來看幾個注意點:

  • new底層做的事情:

    • 在堆內存中申請1塊合適大小的空間

    • 在這塊內存上根據類模版創建對象。類模版中定義了什麼屬性就依次把這些屬性聲明在對象中;對象中還存在一個屬性叫做isa,是一個指針,指向對象所屬的類在代碼段中地址

    • 初始化對象的屬性。這裏初始化有幾個原則:a、如果屬性的數據類型是基本數據類型則賦值爲0;b、如果屬性的數據類型是C語言的指針類型則賦值爲NULL;c、如果屬性的數據類型爲OC的指針類型則賦值爲nil。

    • 返回堆空間上對象的地址

  • 注意

    • 對象只有屬性,沒有方法。包括類本身的屬性和一個指向代碼段中的類isa指針

    • 如何訪問對象的屬性?指針名->屬性名;本質:根據指針名找到指針指向的對象,再根據屬性名查找來訪問對象的屬性值

    • 如何調用方法?[指針名 方法];本質:根據指針名找到指針指向的對象,再發現對象需要調用方法,再通過對象的isa指針找到代碼段中的類,再調用類裏面方法

  • 爲什麼不把方法存儲在對象中?

    • 因爲以類爲模版創建的對象只有屬性可能不相同,而方法相同,如果堆區的對象裏面也保存方法的話就會很重複,浪費了堆空間,因此將方法存儲與代碼段

    • 所以一個類創建的n個對象的isa指針的地址值都相同,都指向代碼段中的類地址

做個小實驗

#import <Foundation/Foundation.h>
@interface Person : NSObject{
    @public
    int _age;
    NSString *_name;
    int *p;
}

-(void)sayHi;
@end

@implementation Person

-(void)sayHi{
    NSLog(@"Hi, %@",_name);
}

@end

int main(int argc, const char * argv[]) {
    Person *p1 = [Person new];
    Person *p2 = [Person new];
    Person *p3 = [Person new];
    p1->_age = 20;
    p2->_age = 20;

    [p1 sayHi];
    [p2 sayHi];
    [p3 sayHi];

    return 0;
}
Person  *p1 = [Person new];

這句代碼在內存分配原理如下圖所示

p1

結論

p1

p3

可以 看到Person類的3個對象p1、p2、p3的isa的值相同。

發佈了42 篇原創文章 · 獲贊 6 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章