3

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

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

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

  1. 首先,我不能添加带有类别的新班级成员,如果我不能这样做,事情会变得有点复杂。
  2. 据我所知,通过类别添加的方法可用于类的所有实例。虽然严格来说这不是一个问题,但它并不适合我,因为我想添加的方法的某些方面会在一般环境中中断;即它们在某种程度上特定于我需要对这些类型的对象做什么。

我还在学习Objective-C。我知道论坛中有一些类似的问题,但答案要么导致类别和死胡同,要么在没有太多帮助的情况下死胡同,所以我觉得我应该简单地重新发布。

4

4 回答 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 构造函数和复制构造函数)。

我也考虑过使用类别,但这似乎不对...

那么你的直觉可能是对的。这种语言特性经常被滥用。

于 2011-05-29T22:14:03.460 回答
1

假设ClassA,ClassBClassC定义如下:

@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>ClassCClassA<ClassCProtocol>-protocolMethodC

首先要注意的是,在 Objective-C 中,实际上并不存在与 C++ 中相同意义上的派生类:只有单一继承,所以我们通常谈论ClassB成为 的子类ClassA,或者ClassC作为 的子类ClassA

然后使用以下代码,假设MDLoadObject()是一个函数,它将返回一个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

由于类ClassBClassC是的最高共同祖先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 {

   }
}
于 2011-05-29T21:48:38.960 回答
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 的实例。

于 2011-05-29T18:06:55.443 回答
0

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

于 2011-05-29T18:07:34.997 回答