Objective-C继承;从父类向派生类进行向下转型/复制

3
在我的程序中,我有一个类,叫做ClassA。我想创建一个派生类,叫做ClassB。我的程序有返回ClassA实例的函数,在某些情况下,我想使用这些返回值来创建ClassB的实例。简单地向下转换并不会导致编译器错误,但会导致运行时错误。这似乎与指针和对象之间的转换问题有关——从我所读的资料来看,在这种情况下这样做是错误的。
然后我尝试模仿C++的复制构造函数,但复制的变量超出了范围或被释放,再次导致运行时错误。
我还考虑过使用分类,但由于以下两个原因,这似乎不合适:
1.首先,我不能使用分类添加新的类成员,如果我不能这样做,事情会变得有些复杂。
2.据我所知,通过分类添加的方法对所有实例都可用。虽然这不是严格意义上的问题,但它让我感到不舒服,因为我想要添加的方法的某些方面在通常情况下会出现问题;即它们与我需要做的这些类型的对象有关。
我仍在学习Objective-C。我知道论坛中有一些类似的问题,但是回答要么指向分类而走到了死路,要么没有提供太多帮助而走到了死路,所以我觉得我应该重新发布这个问题。
4个回答

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 构造函数和复制构造函数)。
我也考虑过使用类别,但这似乎不对...
那么你的直觉可能是正确的。这种语言特性经常被滥用。

目前 MONClassB 的超类(即没有定义超类)是什么? - NSGod

1

听起来好像有一个方法返回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

你可以使用原始方法返回的ClassA实例来创建一个ClassB实例。这样就可以将原始的ClassA实例包装成一个ClassB实例。

这似乎可以行得通。我唯一的担忧是,由于我没有编写ClassB,它的结构可能会在以后的修订中发生变化,那么我就必须回去重写这个类。虽然我目前不认为这会成为一个问题,但我很好奇是否有办法避免这种情况。 - Paul
@Paul:“我唯一的担忧是,因为我没有编写ClassB……”。在你的问题中,你说“我想创建一个派生类,比如ClassB”,这让人觉得你确实对它有完全的控制权。也许你可以编辑你的问题,使它更加清晰明了? - NSGod

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

为了增加趣味性,我还定义了一个名为ClassCProtocol@protocol,它继承自<NSObject>协议,以及一个ClassC对象,它是ClassA的子类并符合<ClassCProtocol>协议(这实际上意味着符合该协议的任何对象都保证实现了-protocolMethodC方法)。
首先要注意的是,在Objective-C中,没有像C++中那样真正意义上的派生类:只有单一继承,因此我们通常说ClassBClassA的子类,或者ClassCClassA的子类。
然后看下面的代码,假设MDLoadObject()是一个函数,根据任何情况返回ClassAClassBClassC的实例:
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 {

   }
}

0

你尝试过使用id类型吗?任何类型的对象都可以分配给它。


我不太确定你的意思。也许是我太笨了,请你提供一些例子吗? - Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接