2009-12-01 2 views
11

둘 이상의 클래스의 객체를 받아들이거나 반환 할 수있는 코드를 구현할 때마다 항상 가장 구체적인 수퍼 클래스를 사용하려고합니다. 예를 들어, 입력에 따라 NSArray * 또는 NSDictionary *를 반환 할 수있는 메서드를 구현하려는 경우 해당 메서드는 NSObject *의 반환 형식을 제공합니다.이 메서드는 가장 직접적인 일반 수퍼 클래스이므로 NSObject *의 반환 형식을 지정합니다. 나는 특정 메소드 서명 애플의 용도 (ID)가 (NSObject의 *)이 더 정확한 것 재단과 다른 API의 많은 경우를 발견했습니다(NSObject *)가 더 정확할 때 메소드 서명에서 (id)를 사용하는 이유는 무엇입니까?

@interface MyParser() 
- (BOOL)stringExpressesKeyValuePairs:(NSString *)string; 
- (BOOL)stringExpressesAListOfEntities:(NSString *)string; 
- (NSArray *)parseArrayFromString:(NSString *)string; 
- (NSDictionary *)parseDictionaryFromString:(NSString *)string; 
@end 

@implementation MyParser 
- (NSObject *)parseString:(NSString *)string { 
    if ([self stringExpressesKeyValuePairs:string]) { 
     return [self parseDictionaryFromString:string]; 
    } 
    else if ([self stringExpressesAListOfEntities:string]) { 
     return [self parseArrayFromString:string]; 
    } 
} 
// etc... 
@end 

예를 들면 다음과 같습니다이다. 예를 들어, 다음 NSPropertyListSerialization하는 방법이다 :이 방법에서

+ (id)propertyListFromData:(NSData *)data 
      mutabilityOption:(NSPropertyListMutabilityOptions)opt 
        format:(NSPropertyListFormat *)format 
      errorDescription:(NSString **)errorString 

가능한 반환 형식이있는 NSData,있는 NSString, NSArray를,있는 NSDictionary,있는 NSDate와의 NSNumber이다. 호출자가 타입 캐스팅없이 retain과 같은 NSObject 메소드를 호출 할 수 있기 때문에 (NSObject *)의 리턴 타입이 (id)보다 더 나은 선택이 될 것 같습니다.

나는 일반적으로 공식 프레임 워크에 의해 설정된 관용구를 모방하려고하지만, 나는 또한 무엇이 그들을 동기 부여 하는지를 알고 싶다. 애플은 이런 경우에 (id)를 사용하는 몇 가지 유효한 이유가 있다고 확신하지만, 나는 그것을 보지 않고있다. 내가 뭘 놓치고 있니?

+0

NSProxy는 NSObject가 아닙니다. NSObject는 유일한 루트 클래스가 아니므로 모든 것이 그 서브 클래스가된다는 것은 안전한 가정이 아닙니다. –

답변

8

id를 사용하면 컴파일러에게 알려지지 않은 유형의 객체가됩니다. NSObject를 사용하면 컴파일러는 NSObject에서 사용할 수있는 메시지 만 사용할 것으로 기대합니다. 그래서 ... 배열이 반환되고 id로 캐스팅 된 것을 알고 있다면 컴파일러 경고없이 objectAtIndex :를 호출 할 수 있습니다. NSObject의 캐스트와 함께 반환하는 반면, 당신은 경고를 얻을 것이다.

1

캐스팅하지 않고 이미 유형 ID의 포인터에 대해 -retain을 호출 할 수 있습니다. 특정 수퍼 클래스 유형을 사용하는 경우 컴파일러 경고를 피하기 위해 서브 클래스의 메서드를 호출 할 때마다 포인터를 캐스팅해야합니다. id를 사용하면 컴파일러에서 경고하지 않고 의도를 더 잘 나타내줍니다.

19

(ID)가 메소드 선언에 사용되는 이유는 두 배이다

(1)에있어서 취할 또는 임의의 유형을 리턴 할 수있다. NSArray는 임의의 임의의 객체를 포함하므로 objectAtIndex:은 임의의 유형의 객체를 반환합니다. 두 번째 이유로 NSObject* 또는 id <NSObject>으로 전송하면 올바르지 않습니다. 첫째, 배열은 특정 작은 메소드 세트를 구현하는 한 비 NSObject 서브 클래스를 포함 할 수 있으며 두 번째로 특정 반환 유형은 형 변환이 필요합니다.

(2) Objective-C는 공변 선언을 지원하지 않습니다. 고려 :

@interface NSArray:NSObject 
+ (id) array; 
@end 

지금, 당신은 NSArrayNSMutableArray 모두 +array를 호출 할 수 있습니다. 전자는 불변의 배열을 돌려 주어, 후자는 변경 가능한 배열을 돌려줍니다. Objective-C가 공변 선언을 지원하지 않기 때문에 위의 코드가 (NSArray*)으로 반환 되었다면 하위 클래스 메소드의 클라이언트는 (NSMutableArray *)로 캐스트해야합니다. 추악하고 허약하고 오류가 발생하기 쉽습니다. 따라서 제네릭 형식을 사용하는 것이 일반적으로 가장 간단한 솔루션입니다.

그래서 ... 특정 클래스의 인스턴스를 반환하는 메서드를 선언하는 경우 명시 적으로 형 변환합니다. 오버라이드 (override)되는 메소드를 선언하고있어, 오버라이드 (override)가 서브 클래스 를 돌려주는 경우, 서브 클래스를 돌려주는 팩토리는 클라이언트에 공개되고 나서 (id)를 사용합니다.

버그를 신고 할 필요가 없습니다. 이미 몇 가지가 있습니다. ObjC 이제 instancetype 키워드를 통해 공동 분산 지원을 제한했다고


참고.

e.e. NSArray의 + 배열 방법은 지금과 같이 선언 될 수있다 :

+ (instancetype) array; 

NSMutableArray*[NSArray array] 동안이 NSArray*을 반환하는 것으로 간주 될 수 반환으로 컴파일러는 [NSMutableArray array]을 치료하는 것입니다.

1

(id) 또한 종종 객체를보다 쉽게 ​​서브 클래 싱 할 수 있도록 반환됩니다. 예를 들어, 이니셜 라이저 및 편의 메소드에서 return (id)은 특별한 이유가없는 한 모든 서브 클래스가 수퍼 클래스의 메소드를 대체하지 않아도된다는 것을 의미합니다.