copy和mutableCopy
示例中要打印引用計(jì)數(shù)(retainCount),所以關(guān)閉了ARC。

示例都寫(xiě)在ViewController中,因此只在這個(gè)文件后面設(shè)置了-fno-objc-arc
1 NSString & NSMutableString
1.1 不可變字符串(NSString)的copy和mutableCopy
NSString *str = @"123";
NSLog(@"str:%p, str.rCount:%lu", str, (unsigned long)str.retainCount);
NSString *str_strCopy = [str copy];
NSLog(@"str_strCopy:%p, str.rCount:%lu", str_strCopy, (unsigned long)str.retainCount);
NSMutableString *muStr_strCopy = [str copy];
NSLog(@"muStr_strCopy:%p, str.rCount:%lu", muStr_strCopy, (unsigned long)str.retainCount);
NSMutableString *muStr_strMuCopy = [str mutableCopy];
NSLog(@"muStr_strMuCopy:%p, str.rCount:%lu", muStr_strMuCopy, (unsigned long)str.retainCount);

- 對(duì)
NSString進(jìn)行copy,其實(shí)就是將指針指向了數(shù)據(jù)源的地址。而且類(lèi)型都是常量字符串類(lèi)型(__NSCFConstantString*)。 -
mutableCopy,則是在堆區(qū)新分配的內(nèi)存空間,并將指針指向了這塊內(nèi)存區(qū)域。類(lèi)型也變成了可變的字符串類(lèi)型(__NSCFString*) - 注意:打印的
retainCount都是數(shù)據(jù)源str的。目的想了解通過(guò)copy或者mutableCopy后,對(duì)數(shù)據(jù)源的引用計(jì)數(shù)會(huì)有什么影響。后續(xù)的案例也同理,都是打印數(shù)據(jù)源的引用計(jì)數(shù)。 - 常量字符串(__NSCFConstantString),保存在數(shù)據(jù)區(qū)中,是不會(huì)被釋放的,所以它的值是
18446744073709551615 = (2^64)-1,也就是ULONG_MAX
1.2 可變字符串(NSMutableString)的copy和mutableCopy
NSMutableString *muStr = [[NSMutableString alloc] initWithString:@"123"];
// NSMutableString *muStr = [[NSMutableString alloc] initWithString:@"可變字符串"];
NSLog(@"muStr:%p, muStr.rCount:%lu", muStr, (unsigned long)muStr.retainCount);
NSString *str_muStrCopy = [muStr copy];
NSLog(@"str_muStrCopy:%p, muStr.rCount:%lu", str_muStrCopy, (unsigned long)muStr.retainCount);
NSMutableString *muStr_muStrCopy = [muStr copy];
NSLog(@"muStr_muStrCopy:%p, muStr.rCount:%lu", muStr_muStrCopy, (unsigned long)muStr.retainCount);
NSMutableString *muStr_muStrMuCopy = [muStr mutableCopy];
NSLog(@"muStr_muStrMuCopy:%p, muStr.rCount:%lu", muStr_muStrMuCopy, (unsigned long)muStr.retainCount);

對(duì)于可變字符串,
copy和mutableCopy都會(huì)重新分配一個(gè)內(nèi)存空間。-
NSTaggedPointerString*是蘋(píng)果在64位操作系統(tǒng)上提出的“小對(duì)象”類(lèi)型。可以將注釋代碼打開(kāi),將第一行代碼注釋掉,也就是設(shè)置一個(gè)復(fù)雜點(diǎn)的字符串。就看不到“小對(duì)象”類(lèi)型了。 引用計(jì)數(shù)方面,由于都是新分配的內(nèi)存空間,對(duì)源數(shù)據(jù)的引用計(jì)數(shù)沒(méi)有影響。
2. NSArray & NSMutableArray
2.1 NSArray的copy和mutableCopy
NSArray *arr = @[[NSMutableString stringWithString:@"1"], @"2"];
NSLog(@"arr:%p, arr.rCount:%lu", arr, (unsigned long)arr.retainCount);
NSArray *arr_arrCopy = [arr copy];
NSLog(@"arr_arrCopy:%p, arr.rCount:%lu", arr_arrCopy, (unsigned long)arr.retainCount);
NSMutableArray *muArr_arrCopy = [arr copy];
NSLog(@"muArr_arrCopy:%p, arr.rCount:%lu", muArr_arrCopy, (unsigned long)arr.retainCount);
NSMutableArray *muArr_arrMuCopy = [arr mutableCopy];
NSLog(@"muArr_arrMuCopy:%p, arr.rCount:%lu", muArr_arrMuCopy, (unsigned long)arr.retainCount);
NSMutableString *t1 = arr[0];
[t1 appendString:@"50"];//@“1”追加@“50” 變成@“150”
NSString * t2 = arr[1];
t2 = @"change";//修改不成功
NSLog(@"muArr:%@", arr);
NSLog(@"arr_muArrCopy:%@", arr_arrCopy);
NSLog(@"muArr_muArrCopy:%@", muArr_arrCopy);
NSLog(@"muArr_muArrMuCopy:%@", muArr_arrMuCopy);

- 不可變的數(shù)組類(lèi)型進(jìn)行
copy操作,返回的還是不可變類(lèi)型(__NSArrayI *)。是不能增加元素、刪除元素和修改元素。會(huì)影響到數(shù)據(jù)源的引用計(jì)數(shù)。 -
mutableCopy操作則是變成了可變數(shù)組類(lèi)型(__NSArrayM *)。地址不一樣,所以不會(huì)影響引用計(jì)數(shù)。 - 雖然不能修改數(shù)組中的元素,但是要知道元素都是指針類(lèi)型,不能修改的是指針指向的地址,但是可以修改地址中的值。第一個(gè)元素是可變字符串,它是在堆區(qū)的,所以我們可以進(jìn)行修改。
- 第二個(gè)元素是常量指針,如果進(jìn)行修改,那么就相當(dāng)于指針指向的地址變了,不可變數(shù)組中是不允許的,所以設(shè)置也沒(méi)有效果。
2.2 NSMutableArray的copy和mutableCopy
NSMutableArray *muArr = [NSMutableArray arrayWithArray:@[[NSMutableString stringWithString:@"1"], @"2"]];
NSLog(@"muArr:%p, muArr.rCount:%lu", muArr, (unsigned long)muArr.retainCount);
NSArray *arr_muArrCopy = [muArr copy];
NSLog(@"arr_muArrCopy:%p, muArr.rCount:%lu", arr_muArrCopy, (unsigned long)muArr.retainCount);
NSMutableArray *muArr_muArrCopy = [muArr copy];
NSLog(@"muArr_muArrCopy:%p, muArr.rCount:%lu", muArr_muArrCopy, (unsigned long)muArr.retainCount);
NSMutableArray *muArr_muArrMuCopy = [muArr mutableCopy];
NSLog(@"muArr_muArrMuCopy:%p, muArr.rCount:%lu", muArr_muArrMuCopy, (unsigned long)muArr.retainCount);
NSMutableString *mt1 = muArr[0];
[mt1 appendString:@"00"];
muArr[1] = @"haha";
NSLog(@"muArr:%@", muArr);
NSLog(@"arr_muArrCopy:%@", arr_muArrCopy);
NSLog(@"muArr_muArrCopy:%@", muArr_muArrCopy);
NSLog(@"muArr_muArrMuCopy:%@", muArr_muArrMuCopy);

-
copy操作會(huì)將可變數(shù)組變化成非可變狀態(tài),狀態(tài)轉(zhuǎn)變,就會(huì)重新開(kāi)辟內(nèi)存空間。但是不會(huì)影響數(shù)據(jù)源的引用計(jì)數(shù) -
mutableCopy和之前的情況一樣。 - 由于數(shù)據(jù)源是可變的狀態(tài),所以數(shù)組中的元素是可以增刪改的。
2.3 數(shù)組中的元素
數(shù)組中的元素都是指針類(lèi)型,經(jīng)過(guò)copy和mutableCopy之后的數(shù)組里的元素的指針地址與源數(shù)組的元素地址相同。但是當(dāng)遇到開(kāi)辟空間的時(shí)候,會(huì)影響元素的引用計(jì)數(shù)。原因是元素又加入到新的數(shù)組中,引用計(jì)數(shù)當(dāng)然要進(jìn)行+1處理:
NSArray *arr = @[[NSMutableString stringWithString:@"1"], @"2"];
NSMutableString * str = arr[0];
NSLog(@"str:%p, str.rCount:%lu", str, (unsigned long)str.retainCount);
NSArray *arr_arrCopy = [arr copy];
NSLog(@"str:%p, str.rCount:%lu", str, (unsigned long)str.retainCount);
NSMutableArray *muArr_arrCopy = [arr copy];
NSLog(@"str:%p, str.rCount:%lu", str, (unsigned long)str.retainCount);
NSMutableArray *muArr_arrMuCopy = [arr mutableCopy];
NSLog(@"str:%p, str.rCount:%lu", str, (unsigned long)str.retainCount);

3. 總結(jié)
-
copy和mutableCopy方法含義上來(lái)說(shuō)都是淺拷貝. - 在上述的示例中,對(duì)容器拷貝,生成一個(gè)新的副本容器.而里面的元素不會(huì)進(jìn)行重新生成,而是引用的方式,這和一個(gè)對(duì)象賦值另一個(gè)對(duì)象沒(méi)有什么不同.
- 如果想要進(jìn)行深拷貝,可以使用"歸檔"NSKeyedArchive類(lèi),或者寫(xiě)文件的方式.
| 源對(duì)象類(lèi)型 | 拷貝方式 | 目標(biāo)對(duì)象類(lèi)型 | 是否影響源對(duì)象引用計(jì)數(shù) |
|---|---|---|---|
| 不可變對(duì)象 | copy | 不可變對(duì)象 | 影響 |
| 不可變對(duì)象 | mutableCopy | 可變對(duì)象 | 不影響 |
| 可變對(duì)象 | copy | 不可變對(duì)象 | 不影響 |
| 可變對(duì)象 | mutableCopy | 可變對(duì)象 | 不影響 |
- 只有不可變對(duì)象進(jìn)行copy,目標(biāo)對(duì)象還是不可變類(lèi)型。不會(huì)開(kāi)辟內(nèi)存空間。其他的都會(huì)開(kāi)辟空間。
4. 面試
面試中經(jīng)常會(huì)問(wèn)NSString類(lèi)型的屬性用strong修飾還是copy修飾。
答案肯定是用copy
- 源數(shù)據(jù)如果是
NSSring類(lèi)型,這兩種方式?jīng)]有區(qū)別。因?yàn)镹SString底層是常量字符串,copy后類(lèi)型不變化。引用計(jì)數(shù)是最大值,strong對(duì)它也沒(méi)有影響。 - 但是源數(shù)據(jù)如果是
NSMutableString類(lèi)型:-
strong修飾后修改引用計(jì)數(shù),屬性和源數(shù)據(jù)指向相同的地址空間,一個(gè)修改另一個(gè)就跟著修改。 -
copy修飾,屬性接受到的是不可變類(lèi)型,也就是NSString,而且開(kāi)辟了新的內(nèi)存空間。屬性和源數(shù)據(jù)指向不同地方。這樣就不會(huì)出現(xiàn)聯(lián)動(dòng)的情況了。
-
綜上所訴,NSString屬性應(yīng)該是用copy修飾。
