在編寫Objective-C
代碼時系草,很多時候會需要對錯誤進行處理刻炒,在OC
里使用的是NSError
。當(dāng)我們編寫一個方法時域滥,比如進行一個網(wǎng)絡(luò)請求,這個時候會有請求成功或請求失敗兩種情況蜈抓。當(dāng)請求失敗時启绰,我們會在方法中生成一個錯誤并告訴調(diào)用方。
在C語言里沟使,返回數(shù)據(jù)有兩種方式委可,一種是常用的return
返回,這是大部分情況下從函數(shù)返回數(shù)據(jù)的方式腊嗡,但C語言里一個函數(shù)只能返回一個數(shù)據(jù)着倾,也就是return
后面只能有一個指針或值(包括結(jié)構(gòu)體)。如果想要返回多個數(shù)據(jù)時燕少,有時會考慮使用參數(shù)返回的形式卡者。
最典型的例子就是交換兩個數(shù)值的函數(shù):
int a = 1, b = 2;
swap(&a, &b);
void swap(int *a, int *b) {
int tmp = *a; *a = *b; *b = tmp; //或者直接*a ^= *b ^= *a ^= *b;
}
交換外部變量的方式很多,如指針客们、引用崇决、位運算,但不能直接使用變量镶摘,這是因為形參和實參的區(qū)別嗽桩,具體不用多說岳守。此處想強調(diào)的是凄敢,不使用return
語句,返回兩個交換后的變量值湿痢,可以使用這種參數(shù)指針的形式涝缝。
鋪墊這些,是為了說明NSError
的使用情況譬重。在OC中錯誤生成的常見形式是這樣:
-(id)requestWithParameter:(id)obj error:(NSError *__autoreleasing *)error {
id result = ... //使用obj參數(shù)進行網(wǎng)絡(luò)請求并返回
if (result != nil) {
return result;
} else {
if (error != NULL) {//判斷調(diào)用方是否需要獲取錯誤信息
*error = [NSError errorWithDomain:...]; //生成錯誤對象
}
return nil;
}
}
對比發(fā)現(xiàn)拒逮,交換指針的方法使用的是單指針作為參數(shù),直接交換了指針指向的內(nèi)容臀规;而錯誤生成的案例中滩援,錯誤參數(shù)使用的是指針的指針。
實際上塔嬉,他們的本質(zhì)是一樣的玩徊。
我們先考慮這種情況:
-(id)requestWithParameter:(id)obj error:(NSError __autoreleasing *)error {
id result = ... //使用obj參數(shù)進行網(wǎng)絡(luò)請求并返回
if (result != nil) {
return result;
} else {
if (error != NULL) {//判斷調(diào)用方是否需要獲取錯誤信息
error = [NSError errorWithDomain:...]; //生成錯誤對象
}
return nil;
}
}
上面的代碼會發(fā)生什么租悄?如果在上面代碼的基礎(chǔ)上在外部創(chuàng)建一個錯誤對象然后調(diào)用方法,最后打佣鞲ぁ:
NSError *error;
[self doSomethingWithObj:nil error:error];
NSLog(@"error: %@", error);
此時打印結(jié)果會是什么泣棋?運行一下會發(fā)現(xiàn)控制臺輸出:
error: (null)
也就是說方法中創(chuàng)建的新的NSError
實例并沒有傳遞給外部的對象指針。其實分析一下可知道畔塔,error = [NSError errorWithDomain:...]; //生成錯誤對象
此處只是將新的實例指針分配給了error這個方法內(nèi)的局部指針變量潭辈,而這個局部指針變量是外部指針變量的一個拷貝。
當(dāng)方法調(diào)用結(jié)束澈吨,外部并沒有對這個新實例的強引用把敢,因此也就會被釋放掉。同時外部的NSError
指針也無法指向這個新的對象谅辣。
這就好比那個指針交換數(shù)值的例子技竟,將其轉(zhuǎn)換成錯誤的值傳遞:
void swap(int a, int b) {
int tmp = a; a = b; b = tmp;
}
此處只是對局部變量a, b進行了交換,函數(shù)出棧后屈藐,這兩個局部變量都被釋放榔组,而外部的變量值并不改變。
回到NSError
联逻,為了能夠?qū)⒎椒ㄖ袆?chuàng)建的NSError
實例分配給外部的那個error
指針指向的地址搓扯,我們需要將外部指針變量存儲的地址直接傳遞給方法進行值拷貝,而不是傳遞指針變量本身包归。這是因為如果傳遞指針變量本身的話锨推,方法只會拷貝一個指針,雖然拷貝后的指針和外部的指針都指向同一個地址公壤,但是指針本身的地址是不同的换可。
而如果傳遞&error
,即取error指針本身的地址厦幅,則是單純的值拷貝(但實際情況略有區(qū)別沾鳄,稍復(fù)雜一些,因為此處增加了__autorelease
關(guān)鍵字确憨,將指針對象自動入池译荞,這個過程會對指針地址做一些處理,導(dǎo)致拷貝的地址會有偏移)休弃,會保留這個指針的地址并在方法內(nèi)部恢復(fù)指針吞歼,同時新建NSError
實例并取地址給這個指針。
類似的塔猾,我們可以寫一個交換NSError
的方法來跟交換數(shù)值的方法進行對比理解:
- (void)swapError:(NSError **)a with:(NSError **)b {
NSError *tmp = *a;
*a = *b;
*b = tmp;
}
因此篙骡,我們可以得出一個結(jié)論,就是使用參數(shù)傳遞返回OC指針類型的對象時,指針的指針是一種比較方便的處理參數(shù)返回方式糯俗。
如有錯誤望不吝指正慎皱!