目錄:
一楷兽、字符串(String)
1.1签孔、字符串的創(chuàng)建
1.2叉讥、字符串的isa
二、拷貝(copy)
2.1饥追、immutable對(duì)象的copy
2.2图仓、mutable對(duì)象的copy
2.3、淺拷貝與深拷貝
2.4 但绕、單層深拷貝
三救崔、 集合(Collections)
3.1、NSMapTable
3.2捏顺、NSHashTable
3.3六孵、NSPointerArray
一、字符串(String)
看到好幾篇文章都在說(shuō)這道面試題幅骄,字符串差不多是每個(gè)高級(jí)語(yǔ)言必有的劫窒,在實(shí)際項(xiàng)目中也的確是使用的最多類型之一。本文就以此題開(kāi)始我們的內(nèi)存管理的討論拆座。```
dsfdsl
//第一個(gè)
1.NSString *name1 = [NSString stringWithFormat:@"stringTestOne"];
__weak NSString *name2 = name1;
NSLog(@"name1:%@", name1);
NSLog(@"name2:%@", name2);
name1 = nil;
NSLog(@"name1:%@", name1);
NSLog(@"name2:%@", name2);
//第二個(gè)
NSString *a1 = [[NSString alloc] initWithFormat:@"stringTestTwo"];
__weak NSString *a2 = a1;
NSLog(@"a1:%@", a1);
NSLog(@"a2:%@", a2);
a1 = nil;
NSLog(@"a1:%@", a1);
NSLog(@"a2:%@", a2);
//第三個(gè)
NSString *b1 = @"stringTestThree";
__weak NSString *b2 = b1;
NSLog(@"b1:%@", b1);
NSLog(@"b2:%@", b2);
b1 = nil;
NSLog(@"b1:%@", b1);
NSLog(@"b2:%@", b2);
打印結(jié)果:
name1:stringTestOne
name2:stringTestOne
name1:(null)
name2:stringTestOne
a1:stringTestTwo
a2:stringTestTwo
a1:(null)
a2:(null)
b1:stringTestThree
b2:stringTestThree
b1:(null)
b2:stringTestThree
1.1主巍、字符串的創(chuàng)建
NSString *string1 = [NSString stringWithFormat:@"TestString1_BAXIANG"];
NSLog(@"%p",string1);
NSString *string2 = [[NSString alloc] initWithFormat:@"TestString2_BAXIANG"];
NSLog(@"%p",string2);
NSString *string3 = @"TestString3_BAXIANG";
NSLog(@"%p",string3);
打印結(jié)果:
0x7fb6e7d58a40
0x7fb6e7d1c3a0
0x10e6a7280
(1) 關(guān)于stringWithFormat和initWithFormat的區(qū)別如果同學(xué)是從MRC開(kāi)發(fā)者一路過(guò)來(lái)的話理解這個(gè)很簡(jiǎn)單冠息,但是ARC已經(jīng)徹底主導(dǎo)了如今的開(kāi)發(fā),對(duì)引用計(jì)數(shù)這個(gè)概念不需要理解那么苛刻煤禽,stringWithFormat實(shí)際上創(chuàng)建的是一個(gè)加入自動(dòng)釋放池的 (autoreleased)的對(duì)象铐达,主要目的就是延遲釋放,而initWithFormat的對(duì)象就需要遵循我們常嘮叨的內(nèi)存管理黃金法則 誰(shuí)創(chuàng)建誰(shuí)釋放檬果。也就是MRC中的release瓮孙。
通過(guò)po _objc_autoreleasePoolPrint()打印當(dāng)前自動(dòng)釋放池的中對(duì)象(Autorelease pools),剛才我們通過(guò)stringWithFormat創(chuàng)建的字符串對(duì)象0x7fa65a50fdc0 在當(dāng)前釋放池中选脊。
objc[24294]: ##############
objc[24294]: AUTORELEASE POOLS for thread 0x10e6533c0
objc[24294]: 798 releases pending.
objc[24294]: [0x7fa65b800000] ................ PAGE (full) (cold)
objc[24294]: [0x7fa65b800038] ################ POOL 0x7fa65b800038
objc[24294]: [0x7fa65b800040] 0x7fa65a702820 __NSCFString
objc[24294]: [0x7fa65b800048] ################ POOL 0x7fa65b800048
........
//
objc[24294]: [0x7fa65b010958] 0x7fa65a50fdc0 __NSCFString
所以第一個(gè)字符name1 = nil 只是去掉了對(duì)當(dāng)前0x7fa65a50fdc0 字符的指針杭抠。name2還是指向了0x7fa65a50fdc0字符串,所以name2還可以打印出當(dāng)前的字符數(shù)據(jù)恳啥。而關(guān)于通過(guò)打印內(nèi)存地址會(huì)發(fā)現(xiàn)字符串3(0x10e6a7280)會(huì)明顯小于上面二者偏灿,因?yàn)樗莿?chuàng)建在字符串常量區(qū)的,而我們的第一二字符串是創(chuàng)建在堆區(qū)的钝的。所以b2是照樣可以打印出字符串的翁垂。但是如果我們修改第二個(gè)字符串內(nèi)容為string,在64位的蘋(píng)果設(shè)備上調(diào)試硝桩,打印的結(jié)果變成跟我們的第一個(gè)和第二個(gè)字符串結(jié)果一樣的:
NSString *a1 = [[NSString alloc] initWithFormat:@"string"];
__weak NSString *a2 = a1;
NSLog(@"a1:%@", a1);
NSLog(@"a2:%@", a2);
a1 = nil;
NSLog(@"a1:%@", a1);
NSLog(@"a2:%@", a2);
打印結(jié)果:
a1:string
a2:string
a1:(null)
a2:string
我們只是縮短了字符串的長(zhǎng)度沿猜,當(dāng)前的字符串的類就變了 ,更讓人奇怪的是字符縮短后的對(duì)象沒(méi)有isa是空碗脊。也就是當(dāng)前字符串對(duì)象沒(méi)有類啼肩。這就涉及到apple的Tagged Pointer。
1.2字符串的isa
(1)NSTaggedPointerString
NSTaggedPointerString 用指針地址的富余位存儲(chǔ)當(dāng)前變量值衙伶,若對(duì)象指針的最低有效位為1(即奇數(shù))祈坠,則該指針為Tagged Pointer。這種指針不通過(guò)解引用isa來(lái)獲取其所屬類矢劲,而是通過(guò)接下來(lái)三位的一個(gè)類表的索引赦拘。該索引是用來(lái)查找所屬類是采用Tagged Pointer的哪個(gè)類。剩下的60位則存儲(chǔ)數(shù)據(jù)芬沉。對(duì)于NSNumber躺同,小于2^60-1的整數(shù)就都采用Tagged Pointer來(lái)存儲(chǔ),對(duì)于字符串來(lái)說(shuō)所需內(nèi)存小于60位的花嘶,它可以創(chuàng)建一個(gè)Tagged Pointer,所以NSTaggedPointerString是一個(gè)偽裝的對(duì)象蹦漠,里面存儲(chǔ)的不是指針地址而是字符串值椭员,這樣不需要一次真正對(duì)象的內(nèi)存分配,不需要一次間接取值笛园。同時(shí)引用計(jì)數(shù)可以是空指令隘击,因?yàn)闆](méi)有內(nèi)存需要釋放侍芝,所以會(huì)對(duì)性能有顯著的提升。
(2)__NSCFConstantString
字符串常量埋同,是一種編譯時(shí)常量州叠,它的 retainCount 值很大,在控制臺(tái)打印出的數(shù)值則是 18446744073709551615==2^64-1凶赁,測(cè)試證明咧栗,即便對(duì)其進(jìn)行 release 操作,retainCount 也不會(huì)產(chǎn)生任何變化虱肄。相同內(nèi)容的 NSCFConstantString 對(duì)象的地址相同致板,也就是說(shuō)常量字符串對(duì)象是一種單例。這種對(duì)象一般通過(guò)字面值 @"..."咏窿、CFSTR("...") 或者 stringWithString: 方法(需要說(shuō)明的是斟或,這個(gè)方法在 iOS6 SDK 中已經(jīng)被稱為redundant,使用這個(gè)方法會(huì)產(chǎn)生一條編譯器警告集嵌。這個(gè)方法等同于字面值創(chuàng)建的方法)產(chǎn)生萝挤。這種對(duì)象存儲(chǔ)在字符串常量區(qū)。
(3)NSCFString
對(duì)象被存儲(chǔ)在堆上根欧。 __NSCFString 對(duì)象是在運(yùn)行時(shí)創(chuàng)建的一種 NSString 子類怜珍,他并不是一種字符串常量。所以和其他的對(duì)象一樣在被創(chuàng)建時(shí)獲得了 1 的引用計(jì)數(shù)咽块。通過(guò) NSString 的 stringWithFormat 等方法創(chuàng)建的 NSString 對(duì)象一般都是這種類型绘面。