copy和mutableCopy探索

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ì)于可變字符串,copymutableCopy都會(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ò)copymutableCopy之后的數(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é)


  • copymutableCopy方法含義上來(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修飾。

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

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