2009-05-24 122 views
44

這個概念似乎困擾着我。爲什麼NSError對象需要將其指針傳遞給修改對象的方法?例如,不只是傳遞對錯誤的引用做同樣的事情?爲什麼NSError需要雙重間接? (指針指針)

NSError *anError; 
[myObjc doStuff:withAnotherObj error:error]; 

,然後在doStuff:

- (void)doStuff:(id)withAnotherObjc error:(NSError *)error 
{ 
    // something went bad! 
    [error doSomethingToTheObject]; 
} 

爲什麼沒有像大多數其他物體消息模式以上工作的工作?爲什麼必須改爲使用錯誤:(NSError **)錯誤?

回答

75

NSError**模式用於某個方法通常返回某個值,但如果失敗,可能需要返回錯誤對象(類型爲NSError*)。在Objective-C中,一個方法只能返回一種類型的對象,但是這種情況下你想返回兩個。在類C語言中,當您需要返回一個額外的值時,您需要一個指向該類型值的指針,因此要返回NSError*,您需要一個NSError**參數。一個更現實的例子是這樣的:

// The method should return something, because otherwise it could just return 
// NSError* directly and the error argument wouldn't be necessary 
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error 
{ 
    NSArray *result = ...; // Do some work that might fail 
    if (result != nil) { 
    return result; 
    } else { 
    // Something went bad! 
    // The caller might pass NULL for `error` if they don't care about 
    // the result, so check for NULL before dereferencing it 
    if (error != NULL) { 
     *error = [NSError errorWithDomain:...]; 
    } 
    return nil; // The caller knows to check error if I return nil 
    } 
} 

如果你只有一個NSError*參數,而不是一個NSError**然後doStuff將永遠無法傳遞錯誤對象返回給調用者。

6

的替代聲明什麼n8gray說:

因爲你不接收對象發送消息到;你正在創建對象並將其返回。您通常需要指針指向 - NSError * - 可變參數,因爲您一次只能使用return語句,而您已經使用它與NO

+0

嗨,彼得:-)我希望你在新的一年有一個好的開始。在````````````````````````````````我在這裏貼了這個帖不好意思,我還是不太明白:如果我們只傳遞了一個`*`(一個指向對象的指針),那麼就可以對對象進行更改了,對吧?爲什麼我們仍然需要傳遞一個指向對象的指針來改變對象?首先十分感謝。問候。 – Unheilig 2014-01-08 00:03:51

+3

@Unheilig:「如果我們只傳遞一個`*`(指向一個對象的指針),那就足以對對象進行更改了,對嗎?」對。您只需要指向該對象的指針即可將消息發送到該對象。 「爲什麼我們仍然需要傳遞一個指向對象的指針來改變對象呢?」因爲你沒有對對象進行更改;你正在創建一個新對象並將其返回給調用者。你可以通過在調用者給你的地址分配它 - 指向變量的指針,在那裏你將把指針指向對象。 – 2014-01-08 03:41:15

89

很簡單:

如果你傳遞一個指向對象的功能,該功能只能修改什麼指針指向。

如果您將指針傳遞給指向對象的指針,則該函數可以修改指向指向另一個對象的指針。

在NSError的情況下,該函數可能需要創建一個新的NSError對象,並傳回一個指向該NSError對象的指針。因此,您需要雙重間接,以便可以修改指針。

6

一個老問題,但我仍然認爲它的價值將這一在這裏 -

實際的罪魁禍首是NSError。如果您查看其類引用,則不會爲其任何屬性設置setter方法,即domain,code或userInfo。所以沒有辦法,你可以只分配和初始化一個NSError,將它傳遞給該方法,然後在傳遞的NSError對象上填充信息。 (如果有一個setter方法,我們可以通過一個NSError *並在方法中執行類似error.code = 1的操作。)

所以萬一出現錯誤,您必須生成一個新的NSError對象在該方法中,如果你這樣做了,將它傳回給調用者的唯一方法是使用NSError **參數。 (由於上述答案中解釋的原因)

0

我仍然沒有通過閱讀上面的所有答案得到完整的圖片。我在下面做的普通人練習,終於幫助我理解發生了什麼。只是把它放在那裏,以防止其他初學者。

假設你有以下

@interface Class X 
-(void) methodX:(NSMutableArray *)array; 
@end 

在有下列順序代碼的其他部分

ClassX *objectX = [[ClassX alloc] init]; 
NSMutableArray *arrayXX = [@[@(1), @(2)] mutableCopy]; 
//What is stored in arrayXX is the address in the heap at which the NSMutableArray object starts, lets call this address ZZZ 
//array starting at address ZZZ in the heap now contains NSNUmbers @1,@2 
[objectX methodX:array] 

當你調用[objectX methodX:array],什麼是由該方法獲得是副本array。由於數組包含一個地址(即一個指針),拷貝的特殊之處在於收到的是地址爲ZZZ的另一個變量。所以,如果methodX確實[array removeObjectAtIndex:0],那麼從地址ZZZ開始的對象會受到影響(現在只包含一個NSNUmber @(2))。所以,當方法返回時,原始數組也會受到影響。

假設改爲methodX確實array = [@[@(2)] mutableCopy];那麼原始數組不會受到影響。這是因爲你沒有進入ZZZ地址並改變某些東西。相反,您將覆蓋該方法收到的副本中的ZZZ重寫爲不同的地址YYY。 YYY地址是包含一個元素NSNUmber @(2)的NSMUtableArray對象的開始。原始的ZZZ地址仍然包含一個帶有兩個元素的NSMUtableArray。 @(1)和@(2)。所以,當方法返回時,原始數組不受影響。