在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
對應(yīng)的控制臺輸出一定有一行表示這個地址.由于從左往右是從低到高, 看下哪些可能像地址, 目標定位到0x60000024b1e0
和0x60000024b1e8
這兩行, 先打印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ù)組對應(yīng)的真實數(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é), 這就印證了我們的一個假設(shè)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是順序存儲這個事實, 我們假設(shè)NSMutableArray也是順序存儲的. 大膽假設(shè)還需小心求證.
上代碼
NSString *str = @"11";
NSLog(@"%p", str);
NSMutableArray *arrayM = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
[arrayM addObject:str];
}
NSLog(@"arrayM:%p", arrayM);
這里NSMutableArray
和NSArray
不同的地方就是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
, 這是由于擴容導(dǎo)致的
(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)常變.
綜上NSArray
和NSMutableArray
在內(nèi)存上都是順序存儲的, 這可能和我之前想的NSMutableArray
是鏈式存儲的說法有些出入, 要深入研究才能知道真實情況和我們想的有什么區(qū)別. 精通iOS, 從lldb開始.