Objective-C继承;向下转换/从父类复制到派生类

时间:2011-05-29 17:57:06

标签: objective-c inheritance class-design class-hierarchy downcast

在我的课程中,我有一节课,说ClassA。我想创建一个派生类,比如ClassB。我的程序有函数返回ClassA的实例,在某些情况下我想使用这些返回来创建ClassB的实例。做天真的向下转换不会导致任何编译器错误,但运行时错误会蔓延。这似乎与关于转换指针与对象的问题有关 - 无论如何,从我读过的内容来看,这是错误的在这种情况下做。

然后我试图模仿C ++的拷贝构造函数,但是复制的变量传递出范围或被释放,再次导致运行时错误。

我也考虑过使用类别,但这似乎有两个原因:

  1. 首先,我不能在类别中添加新的类成员,如果我不能这样做,事情会有点复杂。
  2. 尽可能接近通过类别添加的方法可用于所有类的实例。虽然严格来说这不是一个问题,但是我并不满意,因为我想要添加的方法的某些方面会在一般情况下中断;即它们在某种程度上与我对这些类型的物体需要做的事情有关。
  3. 我还在学习Objective-C。我知道在论坛上有一些类似的问题,但答案要么导致类别,也没有结束或没有太多帮助,所以我觉得我应该重新发布。

4 个答案:

答案 0 :(得分:4)

  
    
      

在我的课程中,我有一堂课,比如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 :(得分:1)

听起来有一种方法可以返回ClassA的实例,但您希望它返回ClassB的实例。

转换返回值的原因不起作用的原因是该方法返回的实例实际上并不是ClassB的实例 - 它实际上只是ClassA的一个实例。当然,强制转换本身将“正常”,但基础实例没有必要的数据来正确处理ClassB的成员。

假设您不能(或不想)更改方法以返回ClassB的实例,则一个选项(除了Categories)将修改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

然后,您将使用原始方法返回的ClassA实例创建ClassB的实例。这基本上包装了ClassA的原始实例,将其转换为ClassB的实例。

答案 2 :(得分:1)

假设ClassAClassBClassC的定义如下:

@interface ClassA : NSObject {

}
- (void)methodA;
@end

@interface ClassB : ClassA {

}
- (void)methodB;
@end

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

@interface ClassC : ClassA <ClassCProtocol> {

}
@end

为了让事情变得有趣,我还定义了一个名为@protocol的{​​{1}},它继承自ClassCProtocol协议,以及一个<NSObject>对象,它是{的子类{1}}并且符合ClassC协议(所有这些都意味着任何符合协议的对象都可以保证实现ClassA方法)。

首先要注意的是,在Objective-C中,实际上并没有像C ++那样的派生类这样的东西:只有单个继承,所以我们通常会谈到{{1作为<ClassCProtocol>的子类,或-protocolMethodCClassB的子类。

然后使用以下代码,假设ClassA是一个函数,它将根据任何情况返回ClassCClassAMDLoadObject()的实例:

ClassA

由于类ClassBClassC的最高共同祖先是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的实例。 (请记住,在单一继承中,ClassAMDLoadObject()的所有实例也保证为ClassA)的实例。

然后,ClassB方法显示了Objective-C的动态,以及几种方法可以查询此时从ClassC函数返回的对象类型。请注意,ClassA方法也可以在没有强制转换的情况下编写,并且在运行时运行良好:

-loadObject:

答案 3 :(得分:0)

您是否尝试过使用ID类型?可以为其分配任何类型的对象。