標籤:
1.Objective-C的指標世界
OC一直是人感覺比較變態的一門語言,為什麼呢?因為它的每個變數都是指標型,多的都幾乎讓人忘了那個*的存在了。
所以常常a=b這個等號讓我產生錯覺,總以為a從b那拷貝了一份。
然而這的的確確,卻是個指標。a這個變數存的內容是b所在的地址。
比如我定義了一個Student的Class,new了stu1和stu2,並把stu2=stu1,那當兩者任何一個進行修改時,兩個變數的內容都會修改。
Student *stu1=[[Student alloc]init]; [email protected]"stu1"; Student *stu2=stu1; [email protected]"stu2"; NSLog(@"STU1:%@----STU2:%@",stu1.name,stu2.name);//輸出兩個都是stu2
在C/C++語言裡,不習慣用指標作變數的情況下,當然兩者是不一樣的,實際上=等號是做了一份拷貝。
Student stu1; stu1.name="stu1"; Student stu2=stu1; stu2.name="stu2"; cout<<"stu1:"<<stu1.name<<" stu2:"<<stu2.name<<endl;//輸出前者stu1,後者stu2
但是上面的情況建立的是C/C++語言裡所謂的棧對象,但棧的大小很有限,在建立堆對象(指標對象)的情況下,就和OC十分相似了
(然而教科書裡教我們建立的貌似都是棧對象)
Student *stu1= new Student(); stu1->name="stu1"; Student *stu2=stu1; stu2->name="stu2"; cout<<"stu1:"<<stu1->name<<" stu2:"<<stu2->name<<endl;//輸出兩個都是stu2
在Java裡面,雖然變數的建立前面沒有*,但其實預設產生的也是堆對象,對象給變數賦值只是一個引用。
2.NSString的錯覺
但是對NSString,我們容易產生錯覺,比如我這樣寫:
NSString *[email protected]"str1"; NSString *str2=str1; [email protected]"str2"; NSLog(@"str1:%@----str2:%@",str1,str2);//輸出前者str1,後者str2
為毛str2的修改還是不影響str1呢?說好的指標呢?這是因為通過[email protected]""方式賦值,事實上已經重新開闢了記憶體,然後讓str2指向這個新地方,所以str2的修改肯定就不會影響str1。
可以通過輸出指標所在地址和指標指向地址來進行分析:
NSString *[email protected]"str1"; NSString *str2=str1; NSLog(@"str1:%p----str2:%p",&str1,&str2);//指標地址,顯示不一樣,這是兩個不一樣的指標 NSLog(@"str1:%p----str2:%p",str1,str2);//內容地址,顯示一樣,說明都指向@“str1” [email protected]"str2"; NSLog(@"str1:%p----str2:%p",&str1,&str2);//指標地址,顯示不一樣,這是兩個不一樣的指標 NSLog(@"str1:%p----str2:%p",str1,str2);//內容地址,顯示不一樣,說明str1所指內容不變,str2所指內容已經變化 NSLog(@"str1:%@----str2:%@",str1,str2);//輸出前者str1,後者str2
以下的方法,實際上也都並不能拷貝一份,因為編譯器對此做了最佳化,使幾個指標變數指向的同一個記憶體地區
NSString *[email protected]"hello"; NSString *string2 = [[NSString alloc]initWithString:string1]; NSString *string3 = [NSString stringWithString:string1];
可見,NSString類變數都是遵循這樣的準則:"指標A賦值給指標B,兩者指向同一塊記憶體,修改後重新指向開闢新記憶體",
3.到底有沒有拷貝?淺拷貝?深拷貝?
(1)NSString到NSString的copy:(編譯器做了最佳化,其實並無copy,跟直接指向是一樣的)
即便是通過以下的Copy,實際上和上面的情況一樣,也只是把str1指向地址拷貝給了str2,使它們指向了同一個@"str1"
NSString *[email protected]"aaa"; NSString *str2=[str1 copy]; NSLog(@"str1:%p----str2:%p",&str1,&str2);//指標地址輸出,不一樣,說明指標建立了 NSLog(@"str1:%p----str2:%p",str1,str2);//指標指向內容地址輸出,是一樣的,說明內容沒有被拷貝
所以,在NSString給NSString賦值時,雖然地址一樣,但一旦修改就會重新指向,所以任何方式相互都不會產生幹擾,以下方法都是等價的,並不一定樣要copy,其實直接用第一種方式比較快。
NSString *str2=str1;
NSString *str3 = [[NSString alloc]initWithString:str1];
NSString *str4 = [NSString stringWithString:str1];
NSString *str5 = [str1 copy];
如果情境需要,比如為了讓某個str1指向的地方銷毀,要先拷貝到新地址,然後用str2指,可以對NSString進行拷貝。
(2)NSString到NSString的拷貝:
NSString *[email protected]"aaa"; NSString *str2=[str1 mutableCopy]; NSLog(@"str1:%p----str2:%p",&str1,&str2);//指標地址輸出,不一樣,說明指標建立了 NSLog(@"str1:%p----str2:%p",str1,str2);//指標指向內容地址輸出,是不一樣的,說明重新開闢了記憶體,把str1的內容拷貝到新記憶體,str2變數指向這個新地址
(3)NSMutableString到NSString的直接指向:
NSMutableString *mStr=[NSMutableString stringWithString:@"abc"]; NSString *str=mStr; NSLog(@"mstr:%p-----str:%p",mStr,str);//指標指向內容地址一樣,說明mStr的改變會聯動str [mStr appendString:@"def"]; NSLog(@"mstr:%@-----str:%@",mStr,str);//mStr的改變影響了str
這種情況,mStr的改變會影響str,但str改變就會重新指向,並不會影響mStr
(4)NSMutableString到NSString的拷貝:
NSMutableString *mStr=[NSMutableString stringWithString:@"abc"]; NSString *str=[mStr copy];//NSString *str=[mStr mutableCopy];也能實現
NSLog(@"mstr:%p-----str:%p",mStr,str);
如果不希望mStr改變影響str,可以對mStr的內容進行拷貝。
但這裡面,[mStr copy]和[mstr mutableCopy],雖然都對原mStr進行了拷貝,開闢了新記憶體地區,但區別在於前者拷貝的對象是不可變的,後者的是可變的。可變字串賦值給NSString其實也就喪失動態性了。
(5)NSMutableString到NSMutableString的指向:
NSMutableString *mStr=[NSMutableString stringWithString:@"abc"]; NSMutableString *mStr2=mStr; NSLog(@"mstr:%p-----mstr2:%p",mStr,mStr2); [mStr appendString:@"def"]; NSLog(@"mstr:%@-----mstr2:%@",mStr,mStr2);
這種情況當然是兩者改變都會互相影響了。
(6)NSMutableString到NSMutableString的拷貝:
NSMutableString *mStr=[NSMutableString stringWithString:@"abc"]; NSMutableString *mStr2=[mStr mutableCopy]; NSLog(@"mstr:%p-----mstr2:%p",mStr,mStr2);//指標內容地址不一樣,說明實現了拷貝 [mStr2 appendString:@"def"]; NSLog(@"mstr:%@-----mstr2:%@",mStr,mStr2);//互不相干
此種方式實現了對NSMutableString的拷貝,產生兩個互不相干的可變字串。
那如果通過copy來拷貝,返回不可變字串賦值給可變的mStr2會怎麼樣?
NSMutableString *mStr2=[mStr copy];
沒錯,會崩掉的。
同樣,用NSString給NSMutableString賦值的都是作死行為
NSString *[email protected]"str1"; NSMutableString *mStr=[str1 copy]; [mStr appendFormat:@"abc"];
同樣崩。
總結一下:
在NSString和NSMutableString中:
不變賦給不變,必須Mutablecopy才能完成真正的拷貝。
可變賦給不變,直接指向,不變會隨可變而變,copy和mutablecopy都能完成拷貝。
不變賦給可變,無論什麼指向拷貝方法都是作死行為,必須調stringWithString函數初始化
可變賦給可變,只有Mutablecopy才能真正的拷貝,copy作死。
Objective-C Objective-C中的指標,及對象的直接指向,copy和mutablecopy