문제

나는 두 개의 클래스를 가지고 있는데, 하나는 다른 클래스의 하위 클래스입니다(예: Animal 그리고 Dog).슈퍼클래스에는 몇 가지 초기화 프로그램이 있습니다(예: initAnimal), 하위 클래스에는 몇 가지 초기화 프로그램이 있습니다(예: initDog).문제는 다음과 같은 작업을 수행하는 것이 (컴파일러의 관점에서) 완전히 합법적이라는 것입니다. Dog *adog = [[Dog alloc] initAnimal], 즉.슈퍼클래스 초기화 프로그램을 사용하여 클래스를 초기화합니다.나는 이것을 좋아하지 않습니다. 왜냐면 서브클래스가 초기화되었는지 확인하고 싶은 추가 인스턴스 변수를 가질 수 있기 때문입니다.헤더 파일을 살펴보면 이 문제가 해결되지만 컴파일러가 자동으로 확인하도록 하는 간단한 방법이 있습니까?아주 분명한 뭔가를 놓치고 있다는 느낌이 들지만, 도저히 집어낼 수 없습니다 :-)

업데이트: 그만큼 initDog 그리고 initAnimal 최고의 예는 아니었습니다.나는 두 개의 매우 다른 초기화 프로그램을 의미했습니다(예: init ~을 위한 Animal 그리고 initWithFur ~을 위한 Dog).모든 개에게 모피를 할당하기를 원했다면 초기화 프로그램의 모피 부분을 만들어 아무도 모피 없이는 개 개체를 얻을 수 없도록 했을 것입니다.하지만 슈퍼클래스를 사용하여 인스턴스를 실수로 초기화하는 것은 여전히 ​​쉽습니다. init, 그리고 나는 물에 푹 빠졌습니다.

지정된 초기화 프로그램을 알려주셔서 감사합니다, Jason.이전에는 이런 일이 발생하지 않았지만 슈퍼클래스의 지정된 초기화 프로그램을 오버로드하고 거기에 일부 정상적인 기본값을 설정할 수 있었습니다.그러나 나는 클래스 자체의 초기화 프로그램이 아닌 다른 초기화 프로그램을 사용하는 것을 불법으로 만들 수 있다면 여전히 선호합니다. 더 많은 아이디어가 있을까요?

도움이 되었습니까?

해결책

일반적으로 Objective-C에서는 각 클래스에 대해 지정된 초기화를 생성한 다음 하위 클래스가 동일한 초기화를 사용합니다.따라서 initAnimal 및 initDog를 사용하는 대신 init를 사용하면 됩니다.그런 다음 개 하위 클래스는 자체 init 메서드를 정의하고 상위 클래스에서 지정된 초기화 프로그램을 호출합니다.

@implementation Dog
-(id)init
{
    if( (self = [super init]) ) {  // call init in Animal and assign to self
        // do something specific to a dog
    }
    return self;
}
@end

클래스가 할당 오른쪽에 선언되므로 실제로 initDog 및 initAnimal을 지정할 필요가 없습니다.

업데이트:질문의 추가 정보를 반영하기 위해 답변에 다음을 추가하고 있습니다.

서브클래스가 지정된 초기화 이외의 초기화를 호출하지 않도록 하는 방법에는 여러 가지가 있으며 궁극적으로 선택하는 방법은 전체 디자인에 따라 결정됩니다.Objective-C의 좋은 점 중 하나는 매우 유연하다는 것입니다.여기서는 시작하는 데 도움이 되는 두 가지 예를 들어 보겠습니다.

첫째, 부모 클래스와 다른 지정된 초기화가 있는 하위 클래스를 생성하는 경우 부모의 초기화를 오버로드하고 예외를 발생시킬 수 있습니다.이를 통해 프로그래머는 자신이 클래스의 프로토콜을 위반했다는 사실을 즉시 알 수 있습니다.그러나 귀하는 매우 이렇게 하는 이유는 하위 클래스가 상위 클래스와 동일한 초기화 프로그램을 사용하지 않을 수 있다는 점을 잘 문서화해야 하기 때문입니다.

@implementation Dog
-(id)init
{
    // Dog does not respond to this initializer
    NSAssert( false, @"Dog classes must use one of the designated initializers; see the documentation for more information." );

    [self autorelease];
    return nil;
}

-(id)initWithFur:(FurOptionsType)furOptions
{
    if( (self = [super init]) ) {
        // do stuff and set up the fur based on the options
    }
    return self;
}
@end

또 다른 방법은 원래 예제와 유사한 초기화 프로그램을 사용하는 것입니다.이 경우 상위 클래스의 기본 초기화를 항상 실패하도록 변경할 수 있습니다.그런 다음 상위 클래스에 대한 비공개 초기화를 생성한 다음 모든 사람이 하위 클래스에서 적절한 초기화를 호출하는지 확인할 수 있습니다.이 경우는 분명히 더 복잡합니다.

@interface Animal : NSObject
-(id)initAnimal;
@end

@interface Animal ()
-(id)_prvInitAnimal;
@end

@interface Dog : Animal
-(id)initDog;
@end

@implementation Animal
-(id)init
{
    NSAssert( false, @"Objects must call designated initializers; see documentation for details." );

    [self autorelease];
    return nil;
}

-(id)initAnimal
{
    NSAssert( [self isMemberOfClass:[Animal class]], @"Only Animal may call initAnimal" );

    // core animal initialization done in private initializer
    return [self _prvInitAnimal];
}

-(id)_prvInitAnimal
{
    if( (self = [super init]) ) {
        // do standard animal initialization
    }
    return self;
}
@end

@implementation Dog
-(id)initDog
{
    if( (self = [super _prvInitAnimal]) ) {
        // do some dog related stuff
    }
    return self;
}
@end

여기에서는 Animal 및 Dog 클래스의 인터페이스와 구현을 볼 수 있습니다.Animal은 지정된 최상위 객체이므로 NSObject의 init 구현을 재정의합니다.Animal 또는 Animal의 하위 클래스에 대해 init를 호출하는 사람은 문서를 참조하는 어설션 오류를 받게 됩니다.Animal은 또한 비공개 카테고리에 비공개 초기화 프로그램을 정의합니다.비공개 카테고리는 코드와 함께 유지되며 Animal의 하위 클래스는 super를 호출할 때 이 비공개 초기화 프로그램을 호출합니다.그 목적은 Animal의 슈퍼클래스(이 경우 NSObject)에서 init를 호출하고 필요할 수 있는 일반 초기화를 수행하는 것입니다.

마지막으로 Animal의 initAnimal 메소드의 첫 번째 줄은 수신자가 실제로 일부 하위 클래스가 아닌 Animal이라는 주장입니다.수신자가 Animal이 아닌 경우 프로그램은 어설션 오류로 인해 실패하고 프로그래머는 문서를 참조하게 됩니다.

이는 특정 요구 사항에 맞게 디자인할 수 있는 방법에 대한 두 가지 예일 뿐입니다.그러나 나는 강하게 디자인 제약 조건을 고려하고 이러한 유형의 디자인이 Cocoa 및 대부분의 OO 디자인 프레임워크에서 비표준이므로 실제로 필요한지 확인하는 것이 좋습니다.예를 들어, 다양한 동물의 루트 수준 개체를 만드는 것을 고려하고 대신 동물 프로토콜을 사용하여 모든 다양한 "동물"이 특정 동물 관련 메시지에 응답하도록 요구할 수 있습니다.이렇게 하면 각 동물(및 Animal의 실제 하위 클래스)이 지정된 초기화 자체를 처리할 수 있으며 특정하고 비표준 방식으로 동작하는 슈퍼클래스에 의존할 필요가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top