在編寫(xiě)Objective-C代碼時(shí),很多時(shí)候會(huì)需要對(duì)錯(cuò)誤進(jìn)行處理,在OC里使用的是NSError。當(dāng)我們編寫(xiě)一個(gè)方法時(shí),比如進(jìn)行一個(gè)網(wǎng)絡(luò)請(qǐng)求,這個(gè)時(shí)候會(huì)有請(qǐng)求成功或請(qǐng)求失敗兩種情況。當(dāng)請(qǐng)求失敗時(shí),我們會(huì)在方法中生成一個(gè)錯(cuò)誤并告訴調(diào)用方。
在C語(yǔ)言里,返回?cái)?shù)據(jù)有兩種方式,一種是常用的return返回,這是大部分情況下從函數(shù)返回?cái)?shù)據(jù)的方式,但C語(yǔ)言里一個(gè)函數(shù)只能返回一個(gè)數(shù)據(jù),也就是return后面只能有一個(gè)指針或值(包括結(jié)構(gòu)體)。如果想要返回多個(gè)數(shù)據(jù)時(shí),有時(shí)會(huì)考慮使用參數(shù)返回的形式。
最典型的例子就是交換兩個(gè)數(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;
}
交換外部變量的方式很多,如指針、引用、位運(yùn)算,但不能直接使用變量,這是因?yàn)樾螀⒑蛯?shí)參的區(qū)別,具體不用多說(shuō)。此處想強(qiáng)調(diào)的是,不使用return語(yǔ)句,返回兩個(gè)交換后的變量值,可以使用這種參數(shù)指針的形式。
鋪墊這些,是為了說(shuō)明NSError的使用情況。在OC中錯(cuò)誤生成的常見(jiàn)形式是這樣:
-(id)requestWithParameter:(id)obj error:(NSError *__autoreleasing *)error {
id result = ... //使用obj參數(shù)進(jìn)行網(wǎng)絡(luò)請(qǐng)求并返回
if (result != nil) {
return result;
} else {
if (error != NULL) {//判斷調(diào)用方是否需要獲取錯(cuò)誤信息
*error = [NSError errorWithDomain:...]; //生成錯(cuò)誤對(duì)象
}
return nil;
}
}
對(duì)比發(fā)現(xiàn),交換指針的方法使用的是單指針作為參數(shù),直接交換了指針指向的內(nèi)容;而錯(cuò)誤生成的案例中,錯(cuò)誤參數(shù)使用的是指針的指針。
實(shí)際上,他們的本質(zhì)是一樣的。
我們先考慮這種情況:
-(id)requestWithParameter:(id)obj error:(NSError __autoreleasing *)error {
id result = ... //使用obj參數(shù)進(jìn)行網(wǎng)絡(luò)請(qǐng)求并返回
if (result != nil) {
return result;
} else {
if (error != NULL) {//判斷調(diào)用方是否需要獲取錯(cuò)誤信息
error = [NSError errorWithDomain:...]; //生成錯(cuò)誤對(duì)象
}
return nil;
}
}
上面的代碼會(huì)發(fā)生什么?如果在上面代碼的基礎(chǔ)上在外部創(chuàng)建一個(gè)錯(cuò)誤對(duì)象然后調(diào)用方法,最后打?。?/p>
NSError *error;
[self doSomethingWithObj:nil error:error];
NSLog(@"error: %@", error);
此時(shí)打印結(jié)果會(huì)是什么?運(yùn)行一下會(huì)發(fā)現(xiàn)控制臺(tái)輸出:
error: (null)
也就是說(shuō)方法中創(chuàng)建的新的NSError實(shí)例并沒(méi)有傳遞給外部的對(duì)象指針。其實(shí)分析一下可知道,error = [NSError errorWithDomain:...]; //生成錯(cuò)誤對(duì)象此處只是將新的實(shí)例指針?lè)峙浣o了error這個(gè)方法內(nèi)的局部指針變量,而這個(gè)局部指針變量是外部指針變量的一個(gè)拷貝。
當(dāng)方法調(diào)用結(jié)束,外部并沒(méi)有對(duì)這個(gè)新實(shí)例的強(qiáng)引用,因此也就會(huì)被釋放掉。同時(shí)外部的NSError指針也無(wú)法指向這個(gè)新的對(duì)象。
這就好比那個(gè)指針交換數(shù)值的例子,將其轉(zhuǎn)換成錯(cuò)誤的值傳遞:
void swap(int a, int b) {
int tmp = a; a = b; b = tmp;
}
此處只是對(duì)局部變量a, b進(jìn)行了交換,函數(shù)出棧后,這兩個(gè)局部變量都被釋放,而外部的變量值并不改變。
回到NSError,為了能夠?qū)⒎椒ㄖ袆?chuàng)建的NSError實(shí)例分配給外部的那個(gè)error指針指向的地址,我們需要將外部指針變量存儲(chǔ)的地址直接傳遞給方法進(jìn)行值拷貝,而不是傳遞指針變量本身。這是因?yàn)槿绻麄鬟f指針變量本身的話,方法只會(huì)拷貝一個(gè)指針,雖然拷貝后的指針和外部的指針都指向同一個(gè)地址,但是指針本身的地址是不同的。
而如果傳遞&error,即取error指針本身的地址,則是單純的值拷貝(但實(shí)際情況略有區(qū)別,稍復(fù)雜一些,因?yàn)榇颂幵黾恿?code>__autorelease關(guān)鍵字,將指針對(duì)象自動(dòng)入池,這個(gè)過(guò)程會(huì)對(duì)指針地址做一些處理,導(dǎo)致拷貝的地址會(huì)有偏移),會(huì)保留這個(gè)指針的地址并在方法內(nèi)部恢復(fù)指針,同時(shí)新建NSError實(shí)例并取地址給這個(gè)指針。
類(lèi)似的,我們可以寫(xiě)一個(gè)交換NSError的方法來(lái)跟交換數(shù)值的方法進(jìn)行對(duì)比理解:
- (void)swapError:(NSError **)a with:(NSError **)b {
NSError *tmp = *a;
*a = *b;
*b = tmp;
}
因此,我們可以得出一個(gè)結(jié)論,就是使用參數(shù)傳遞返回OC指針類(lèi)型的對(duì)象時(shí),指針的指針是一種比較方便的處理參數(shù)返回方式。
如有錯(cuò)誤望不吝指正!