2011-07-18 32 views
3

我有一個我寫的不會編譯的核心數據驗證方法。我可以修改該方法,以便編譯...但後來我得到運行時錯誤。該方法的兩個版本如下(注意在第二個版本中缺少'*')。這個版本的編譯,但給人的運行時錯誤「+ [NSCFNumber的doubleValue]:無法識別的選擇發送到類0x7fff70a448e8」:將消息發送到(id *)變量或使用(id *)參數的正確方法

- (BOOL)validateInitialValue:(id *)value error:(NSError **)error { 
    if (*value == nil) { 
     return YES; 
    } 
    if ([*value doubleValue] < 0.0) { 
     return NO; 
    } 
    return YES; 
} 

這個版本提供了編譯器警告和錯誤(見警告和錯誤如下):

- (BOOL)validateInitialValue:(id *)value error:(NSError **)error { 
    if (value == nil) { 
     return YES; 
    } 
    if ([value doubleValue] < 0.0) { 
     return NO; 
    } 
    return YES; 
} 

編譯器錯誤和警告:

warning: Semantic Issue: Receiver type 'id *' is not 'id' or interface pointer, consider casting it to 'id' 
warning: Semantic Issue: Method '-doubleValue' not found (return type defaults to 'id') 
error: Semantic Issue: Invalid operands to binary expression ('id' and 'double') 

我終於想通這個問題可能是在調用代碼:

- (void)setInitialValue:(NSNumber *)initialValue { 
    [self willChangeValueForKey:@"initialValue"]; 
    [self validateValue:initialValue forKey:@"initialValue" error:nil]; 
    [self setPrimitiveInitialValue:initialValue]; 
    [self didChangeValueForKey:@"initialValue"]; 
} 

我更改爲使用「&」來電初值前和使用的方法的第一個版本,和一切工作。因此,新調用的代碼具有一行變化是這樣的:

[self validateValue:&initialValue forKey:@"initialValue" error:nil]; 

但是否真正有必要對「&」? setPrimitiveInitialValue不使用'&'。我覺得我對Objective-C的理解還不夠完善,你所有的專家都會發現這是一個微不足道的問題,並帶有非常直接的答案。

+1

其他已經解釋了'id *'的含義;我只想補充一點,你得到運行時錯誤的原因是Objective-C對象的第一個字段是指向它的類對象的指針。所以當你傳遞'initialValue'作爲'(id *)的值,然後調用'[* value doubleValue]',那就是'[* initialValue doubleValue]',並且指向某個對象/結構的指針與指針相同到它的第一個字段,所以你實際上在* class * NSCFNumber上調用doubleValue - 注意運行時錯誤中的+號,它表示一個類方法。 –

回答

4

你是正確的,問題是調用代碼。 id *指示指針id值。一個對象變量本身是一個id,所以你需要一個指向該變量的指針,這就是你用&得到的。

你傳遞一個指針是這樣,如果您的驗證方法知道的方法來修改值,使之有效,它可以返回YES返回的有效對象(通過設置變量)的原因。因此,舉例來說,如果小於1的數字應被鎖定爲0,你可以這樣做:

- (BOOL)validateInitialValue:(id *)value error:(NSError **)error { 
    if (*value == nil) { 
     return YES; 
    } 
    if ([*value doubleValue] < 0.0) { 
     return NO; 
    } 
    if ([*value doubleValue] > 0.0 && [*value doubleValue] < 1.0) { 
     *value = [NSNumber numberWithInt:0]; 
    } 
    return YES; 
} 

setPrimitiveValue:並不需要調用上下文設置變量,所以它只是需要一個id。 (很少有方法像validateValue:forKey:error:一樣工作,一般情況下,他們會這樣做,如果他們想要返回BOOL來表示他們是否改變了某些東西,但他們仍然需要一種方法來返回更改後的值。)

5

id本身表示一個指針。所以當你使用id *時,你實際上是指向一個指針。 this優秀教程的第一部分解釋了這個概念。

有機會,這是你在找什麼:

- (BOOL)validateInitialValue:(id)value error:(NSError **)error { 
    if (value == nil) { 
     return YES; 
    } 
    if ([value doubleValue] < 0.0) { 
     return NO; 
    } 
    return YES; 
} 
+1

具體而言,'id'是[objc.h](http://opensource.apple.com/source/objc4/objc4-437.3/runtime/objc.h)中的一個typedef,用於指向通用Objective- C對象:'typedef struct objc_object {isa; } * id;'(參見[運行時參考](http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/doc/uid/TP40001418-CH3g-BAJFBCCJ)。) –

+2

此方法是核心數據驗證系統的一部分。參數的正確類型確實是'id *'。 – Chuck

+0

謝謝你的答案,它有幫助,但查克是正確的,我沒有選擇參數類型。我重寫了超類方法,查克的回答解釋了這一點,並且這回答了我所有的問題,所以他得到了這個問題的「檢查」。多謝你們! – Jason

相關問題