探索iOS數(shù)組容器的存儲結(jié)構(gòu)

在C++中我們我們通常使用順序存儲作為數(shù)組的存儲結(jié)構(gòu), 那在OC中, 封裝過的NSArray和NSMutableArray到底是順序存儲還是鏈式存儲的呢, 今天就來一探究竟.

上代碼

    NSString *str = @"11";
    NSLog(@"%p", str);
    NSArray *array = @[str, @"1", @"2"];
    NSLog(@"array:%p", array);

這里用NSArray數(shù)組存放3個元素, 第一個元素是@"11"這個字符串, 我們知道, 字符串是作為常量的, 地址是不會改變的, 并且, 數(shù)組的首地址就是str的地址, 因此我們打印出str的地址.

控制臺輸出如下

0x104f5d078

然后我們在NSLog(@"array:%p", array);這一行下斷點, 并打印當前的內(nèi)存情況

命令 (lldb) x/100xb array

控制臺輸出如下

0x60000024b1c0: 0xc0 0xda 0x1c 0x06 0x01 0x00 0x00 0x00
0x60000024b1c8: 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b1d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b1d8: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b1e0: 0xe0 0x94 0x22 0x00 0x00 0x60 0x00 0x00
0x60000024b1e8: 0x60 0xad 0x03 0x00 0x00 0x60 0x00 0x00
0x60000024b1f0: 0xad 0xbe 0xb7 0x66 0x89 0xf7 0xdd 0xba
0x60000024b1f8: 0x80 0xaf 0x24 0x00 0x00 0x60 0x00 0x00
0x60000024b200: 0xc0 0x79 0x22 0x00 0x00 0x60 0x00 0x00
0x60000024b208: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b210: 0x61 0x63 0x74 0x69 0x6f 0x6e 0x00 0x00
0x60000024b218: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b220: 0xd0 0xb0 0x24 0x00

這里的內(nèi)存主要打印的是NSArray這個"結(jié)構(gòu)體"(所有的OC對象實際都是結(jié)構(gòu)體)在內(nèi)存上的存儲情況.

0x60000024b1c8: 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00

這一行0x03我們猜想是數(shù)組的長度, 感興趣的小伙伴可以換下數(shù)組, 發(fā)現(xiàn)這里確實是數(shù)組的長度, 那數(shù)組里面的真實的數(shù)據(jù)在哪里呢?
首先這些真實數(shù)據(jù)的首地址一定在NSArray這個"結(jié)構(gòu)體"里面有存儲, 也就是x/100xb array對應的控制臺輸出一定有一行表示這個地址.由于從左往右是從低到高, 看下哪些可能像地址, 目標定位到0x60000024b1e00x60000024b1e8這兩行, 先打印0x60000024b1e0的內(nèi)存

命令 (lldb) x/100xb 0x6000002294e0

控制臺輸出如下

0x6000002294e0: 0x78 0xd0 0xf5 0x04 0x01 0x00 0x00 0x00
0x6000002294e8: 0xb8 0xd0 0xf5 0x04 0x01 0x00 0x00 0x00
0x6000002294f0: 0xd8 0xd0 0xf5 0x04 0x01 0x00 0x00 0x00
0x6000002294f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000229500: 0xad 0xbe 0xb7 0x66 0x89 0xf7 0xdd 0xba
0x600000229508: 0xc0 0x93 0x22 0x00 0x00 0x60 0x00 0x00
0x600000229510: 0x0c 0x53 0x77 0x69 0x73 0x73 0x20 0x47
0x600000229518: 0x65 0x72 0x6d 0x61 0x6e 0x00 0x00 0x00
0x600000229520: 0x18 0xe2 0x1c 0x06 0x01 0x00 0x00 0x00
0x600000229528: 0x8c 0x07 0x00 0x00 0x01 0x00 0x00 0x00
0x600000229530: 0x0c 0x47 0x72 0x6f 0x75 0x70 0x2d 0x4e
0x600000229538: 0x6f 0x72 0x64 0x69 0x63 0x00 0x00 0x00
0x600000229540: 0x18 0xe2 0x1c 0x06

看這里的第一行內(nèi)存里的數(shù)據(jù)是0x0104f5d078, 這個數(shù)據(jù)剛好就是str的地址, 因此我們先假0x6000002294e0就是數(shù)組對應的真實數(shù)據(jù)的首地址, 然后分別打印這些對象的真實數(shù)據(jù)

命令 (lldb)po 0x0104f5d078
11
(lldb) po 0x0104f5d0b8
1
(lldb) po 0x0104f5d0d8
2
真的是數(shù)組里面的數(shù)據(jù), 再看下存儲這些對象的內(nèi)存地址

0x6000002294e0
0x6000002294e8
0x6000002294f0

剛好都差8個字節(jié), 我們知道OC里面的數(shù)組存儲的是對象類型, 也就是指針, 指針在64位系統(tǒng)上是8個字節(jié), 這就印證了我們的一個假設NSArray在內(nèi)存上是連續(xù)存儲的.
為了進一步研究, 我們打印剛才NSArray"結(jié)構(gòu)體"中另一個可疑的首地址的內(nèi)存情況0x60000024b1e8

(lldb) x/100xb 0x60000024b1e8
0x60000024b1e8: 0x60 0xad 0x03 0x00 0x00 0x60 0x00 0x00
0x60000024b1f0: 0xad 0xbe 0xb7 0x66 0x89 0xf7 0xdd 0xba
0x60000024b1f8: 0x80 0xaf 0x24 0x00 0x00 0x60 0x00 0x00
0x60000024b200: 0xc0 0x79 0x22 0x00 0x00 0x60 0x00 0x00
0x60000024b208: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b210: 0x61 0x63 0x74 0x69 0x6f 0x6e 0x00 0x00
0x60000024b218: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60000024b220: 0xd0 0xb0 0x24 0x00 0x00 0x60 0x00 0x00
0x60000024b228: 0x20 0xbe 0x23 0x00 0x80 0x60 0x00 0x00
0x60000024b230: 0x55 0xe6 0xf6 0x55 0x35 0x05 0x00 0xa0
0x60000024b238: 0x40 0xd7 0x09 0x00 0x00 0x60 0x00 0x00
0x60000024b240: 0xa0 0x0a 0x28 0x00 0x80 0x60 0x00 0x00
0x60000024b248: 0xf0 0x0a 0x28 0x00

嘗試打印里面的數(shù)據(jù)

(lldb) po 0x60000003ad60
105553116507488

0x60000003ad60好像做不起首地址.

下面研究下NSMutableArray的存儲情況, 根據(jù)NSArray是順序存儲這個事實, 我們假設NSMutableArray也是順序存儲的. 大膽假設還需小心求證.

上代碼

    NSString *str = @"11";
    NSLog(@"%p", str);
    NSMutableArray *arrayM = [NSMutableArray array];
    for (int i = 0; i < 10; i++) {
        [arrayM addObject:str];
    }
    NSLog(@"arrayM:%p", arrayM);

這里NSMutableArrayNSArray不同的地方就是NSMutableArray可能需要擴容

看初始化的內(nèi)存情況

(lldb) x/100xb arrayM
0x600000278b40: 0x98 0x3a 0xde 0x04 0x01 0x00 0x00 0x00
0x600000278b48: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b50: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b58: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b60: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b68: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b70: 0x40 0x38 0x22 0x00 0x00 0x60 0x00 0x00
0x600000278b78: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b80: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x600000278b88: 0x80 0x84 0x27 0x00 0x00 0x60 0x00 0x00
0x600000278b90: 0x80 0xf0 0x25 0x00 0x00 0x60 0x00 0x00
0x600000278b98: 0x00 0x00 0x00 0x00 0x73 0x42 0x61 0x72
0x600000278ba0: 0x70 0x72 0x6f 0x63

0x40 0x38 0x22 0x00 0x00 0x60 可能存的是首地址吧, who care, 執(zhí)行完一次[arrayM addObject:str];后的內(nèi)存

(lldb) x/100xb arrayM
0x600000278b40: 0x98 0x3a 0xde 0x04 0x01 0x00 0x00 0x00
0x600000278b48: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b50: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b58: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b60: 0xa0 0xaa 0x01 0x00 0x80 0x60 0x00 0x00
0x600000278b68: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b70: 0x40 0x38 0x22 0x00 0x00 0x60 0x00 0x00
0x600000278b78: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b80: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x600000278b88: 0x80 0x84 0x27 0x00 0x00 0x60 0x00 0x00
0x600000278b90: 0x80 0xf0 0x25 0x00 0x00 0x60 0x00 0x00
0x600000278b98: 0x00 0x00 0x00 0x00 0x73 0x42 0x61 0x72
0x600000278ba0: 0x70 0x72 0x6f 0x63

繼續(xù)看可疑的首地址

(lldb) x/100xb 0x60800001aaa0
0x60800001aaa0: 0x78 0xb0 0x69 0x03 0x01 0x00 0x00 0x00
0x60800001aaa8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60800001aab0: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x60800001aab8: 0x70 0xaa 0x01 0x00 0x80 0x60 0x00 0x00
0x60800001aac0: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x60800001aac8: 0xa0 0xab 0x01 0x00 0x80 0x60 0x00 0x00
0x60800001aad0: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x60800001aad8: 0x10 0xaa 0x01 0x00 0x80 0x60 0x00 0x00
0x60800001aae0: 0x00 0xaa 0x81 0x41 0xe2 0x7f 0x00 0x00
0x60800001aae8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x60800001aaf0: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x60800001aaf8: 0xb0 0xaa 0x01 0x00 0x80 0x60 0x00 0x00
0x60800001ab00: 0xd8 0x45 0xde 0x04
(lldb) po 0x010369b078
11

果然11進來了
再看執(zhí)行了2次add后的內(nèi)存

(lldb) x/100xb arrayM
0x600000278b40: 0x98 0x3a 0xde 0x04 0x01 0x00 0x00 0x00
0x600000278b48: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b50: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b58: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b60: 0x20 0x38 0x22 0x00 0x00 0x60 0x00 0x00
0x600000278b68: 0x05 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b70: 0x40 0x38 0x22 0x00 0x00 0x60 0x00 0x00
0x600000278b78: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000278b80: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x600000278b88: 0x80 0x84 0x27 0x00 0x00 0x60 0x00 0x00
0x600000278b90: 0x80 0xf0 0x25 0x00 0x00 0x60 0x00 0x00
0x600000278b98: 0x00 0x00 0x00 0x00 0x73 0x42 0x61 0x72
0x600000278ba0: 0x70 0x72 0x6f 0x63

首地址變成了0x20 0x38 0x22 0x00 0x00 0x60, 這是由于擴容導致的

(lldb) x/100xb 0x600000223820
0x600000223820: 0x78 0xb0 0x69 0x03 0x01 0x00 0x00 0x00
0x600000223828: 0x78 0xb0 0x69 0x03 0x01 0x00 0x00 0x00
0x600000223830: 0x78 0xb0 0x69 0x03 0x01 0x00 0x00 0x00
0x600000223838: 0x78 0xb0 0x69 0x03 0x01 0x00 0x00 0x00
0x600000223840: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000223848: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000223850: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000223858: 0xa8 0x70 0xde 0x04 0x01 0x00 0x00 0x00
0x600000223860: 0xad 0xbe 0x4c 0xac 0xb5 0x25 0xdd 0xba
0x600000223868: 0xa0 0x3a 0x22 0x00 0x00 0x60 0x00 0x00
0x600000223870: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000223878: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x600000223880: 0xb0 0xed 0x25 0x00

還是順序存儲的. 有興趣的小伙伴可以繼續(xù)看下執(zhí)行完10次add后的情況, 結(jié)論是:還是順序存儲的, 不過首地址會經(jīng)常變.

綜上NSArrayNSMutableArray在內(nèi)存上都是順序存儲的, 這可能和我之前想的NSMutableArray是鏈式存儲的說法有些出入, 要深入研究才能知道真實情況和我們想的有什么區(qū)別. 精通iOS, 從lldb開始.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • __block和__weak修飾符的區(qū)別其實是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,609評論 0 6
  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理。運行時機制的原理和運用場景。SDWebImage的原...
    LZM輪回閱讀 2,132評論 0 12
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,255評論 0 7
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,580評論 0 35
  • 今天用react開發(fā),準備引入一個組件本來是import Log from Log/index但是我卻寫成了 im...
    鄒小鄒大廚閱讀 376評論 0 0

友情鏈接更多精彩內(nèi)容