Наследование Objective-C;уклонение / копирование из родительского класса в производный класс - PullRequest
3 голосов
/ 29 мая 2011

В моей программе есть класс, скажем ClassA.Я хотел бы создать производный класс, скажем ClassB.В моей программе есть функции, возвращающие экземпляры ClassA, и в некоторых случаях я хотел бы использовать эти возвраты для создания экземпляра ClassB.Выполнение наивного понижения рейтинга не приводит к ошибкам компилятора, но появляются ошибки времени выполнения. Это, по-видимому, связано с проблемами приведения указателей к объектам - во всяком случае, из того, что я прочитал, это было неправильноделать в этом случае.

Затем я попытался имитировать конструкторы копирования C ++, но скопированные переменные вышли из области действия или были освобождены, что снова привело к ошибкам во время выполнения.

Я также рассматривал возможность использования категорий, но это кажется неправильным по двум причинам:

  1. Во-первых, я не могу добавлять новых учеников с категориями, и вещи получатнемного запутанный, если я не могу сделать это.
  2. Насколько я могу судить, методы, добавленные через категории, становятся доступными для всех экземпляров класса.Хотя, строго говоря, это не проблема, но мне это не нравится, поскольку некоторые аспекты методов, которые я хочу добавить, нарушают общие настройки;то есть они так или иначе связаны с тем, что мне нужно делать с этими типами объектов.

Я все еще изучаю Objective-C.Я знаю, что на форуме есть несколько похожих вопросов, но ответы либо приводили к категориям и тупикам, либо тупикам без особой помощи, поэтому я чувствовал, что должен просто перепостить.

Ответы [ 4 ]

4 голосов
/ 30 мая 2011

В моей программе есть класс, скажем ClassA. Я хотел бы создать производный класс, скажем, ClassB.

@class MONClassA;

@interface MONClassB : MONClassA
{
    int anotherVariable;
}

//...
- (id)initWithMONClassA:(MONClassA *)classA anotherVariable:(int)anotherVar;

@end

В моей программе есть функции, возвращающие экземпляры ClassA, и в некоторых случаях я хотел бы использовать эти возвраты для создания экземпляра ClassB.

так же, как вы делали бы это в c ++ (как кажется, это ваш фон), продвигайте тип с новым экземпляром:

//...
MONClassA * a = SomeLibFunction();
MONClassB * b = [[MONClassB alloc] initWithMONClassA:a anotherVariable:???];
//...

Выполнение наивного понижения рейтинга не приводит к ошибкам компилятора, но появляются ошибки времени выполнения. Это, по-видимому, связано с проблемами приведения указателей к объектам - во всяком случае, из того, что я прочитал, это было неправильная вещь в этом случае.

правильно - это неправильно.

Затем я попытался имитировать конструкторы копирования C ++, но скопированные переменные вышли из области видимости или были освобождены, что снова привело к ошибкам во время выполнения.

см. Приведенную выше реализацию, чтобы узнать, как повысить тип в objc. это, конечно, предполагает, что вы также определите подходящую реализацию для инициализации (эквивалентную вашему конструктору cpp и конструктору копирования).

Я также подумывал об использовании категорий, но это не совсем правильно ...

тогда ваш инстинкт, вероятно, прав. эта языковая функция используется довольно часто.

1 голос
/ 30 мая 2011

Допустим, ClassA, ClassB и ClassC определены следующим образом:

@interface ClassA : NSObject {

}
- (void)methodA;
@end

@interface ClassB : ClassA {

}
- (void)methodB;
@end

@protocol ClassCProtocol <NSObject>
- (void)protocolMethodC;
@end

@interface ClassC : ClassA <ClassCProtocol> {

}
@end

Чтобы сделать вещи интересными, я также определил @protocol с именем ClassCProtocol, который наследуется отпротокол <NSObject>, а также объект ClassC, который является подклассом ClassA и соответствует протоколу <ClassCProtocol> (все, что на самом деле означает, что любой объект, соответствующий протоколу, гарантированно реализует -protocolMethodC method).

Первое, на что следует обратить внимание, это то, что в Objective-C на самом деле нет такой вещи, как производный класс в том же смысле, что и в C ++: есть только одно наследование,поэтому мы обычно говорим о ClassB как о подклассе ClassA, или ClassC как о подклассе ClassA.

Затем возьмем следующий код, предполагая, что MDLoadObject() - это функция, которая будет возвращатьэкземпляр ClassA, ClassB или ClassC, основанный на любых обстоятельствах:

ClassA *MDLoadObject() {
    ClassA *object = nil;
    if (...) {
       object = [[[ClassA alloc] init] autorelease];
    } else if (...) {
       object = [[[ClassB alloc] init] autorelease];
    } else {
       object = [[[ClassC alloc] init] autorelease];
    }
    return object;
}

@interface MDAppController : NSObject {

}
- (void)loadObject:(id)sender;
@end

@implementation MDAppController

- (void)loadObject:(id)sender {
   ClassA *instanceOfClassABorC = MDLoadObject();

   if ([instanceOfClassABorC isKindOfClass:[ClassB class]]) {
       [(ClassB *)instanceOfClassABorC methodB];
   } else if ([instanceOfClassABorC isKindOfClass:[ClassC class]]) {
       [(ClassC *)instanceOfClassABorC protocolMethodC];
   } else if ([instanceOfClassABorC respondsToSelector:@selector(protocolMethodC)) {
       [(ClassC *)instanceOfClassABorC protocolMethodC];
   } else {

   }
}
@end

Поскольку самый высокий общий предок классов ClassB и ClassC равен ClassA, мы определяемфункция MDLoadObject() для возврата экземпляра ClassA.(Помните, что в одиночном наследовании все экземпляры ClassB и ClassC также гарантированно являются экземплярами ClassA).

Метод -loadObject:, следовательно, показывает динамизм Objective-C,и несколько способов узнать, какой тип объекта был возвращен функцией MDLoadObject() в данный момент времени.Обратите внимание, что метод -loadObject: также может быть написан без приведения и будет работать нормально во время выполнения:

- (void)loadObject:(id)sender {
   ClassA *instanceOfClassABorC = MDLoadObject();

   if ([instanceOfClassABorC isKindOfClass:[ClassB class]]) {
       [instanceOfClassABorC methodB];
   } else if ([instanceOfClassABorC isKindOfClass:[ClassC class]]) {
       [instanceOfClassABorC protocolMethodC];
   } else if ([instanceOfClassABorC respondsToSelector:@selector(protocolMethodC)) {
       [instanceOfClassABorC protocolMethodC];
   } else {

   }
}
1 голос
/ 29 мая 2011

Звучит так, как будто существует метод, который возвращает экземпляр ClassA, но вы хотите вместо этого вернуть экземпляр ClassB.

Причина, по которой приведение возвращаемого значения не работает, заключается в том, чтоэкземпляр, возвращаемый методом, на самом деле не является экземпляром ClassB - это на самом деле просто экземпляр ClassA.Конечно, само приведение будет «работать», но базовый экземпляр не имеет данных, необходимых для правильной обработки членов ClassB.

Предполагая, что вы не можете (или не хотите)Измените метод, чтобы он возвращал экземпляр ClassB, одна опция (кроме категорий) будет состоять в том, чтобы изменить ClassB для получения экземпляра ClassA.ClassB переопределяет все методы, реализованные ClassA, и переносится на экземпляр ClassA, переданный изначально.Например:

@implementation ClassB
- (id)initWithClassA:(ClassA*)a
{
    self = [super init];
    if(!self)
    {
        return nil;
    }

    myClassA = [a retain];

    return self;
}

- (void)dealloc
{
    [myClassA release];
}

- (void)ClassAMethod1
{
    [myClassA ClassAMethod1];
}

- (int)ClassAMethod2
{
    return [myClassA ClassAMethod2];
}

- (void)ClassBMethod1
{
    // implement the method here
}
@end

Затем вы должны создать экземпляр ClassB, используя экземпляр ClassA, возвращенный исходным методом.По сути, это оборачивает исходный экземпляр ClassA, превращая его в экземпляр ClassB.

0 голосов
/ 29 мая 2011

Вы пытались использовать тип идентификатора?Ему может быть назначен объект любого типа.

...