【问题标题】:Complex class hierarchies in Objective-C++Objective-C++ 中的复杂类层次结构
【发布时间】:2013-09-26 16:21:34
【问题描述】:

考虑以下基类:

@interface ViewBase : UIView
@property (readonly) LayerBase *myLayer;
+ (Class)myLayerClass; // _myLayer = [[[self class] myLayerClass] new];
@end

@interface LayerBase : CALayer
@property AbstractGrid *grid;
@end

class AbstractGrid
{
public:
    int rows, columns;
    virtual someMethod() = 0;
}

我有一个模板Grid 类,它使用不同的单元格类型(需要AbstractGrid,因为不可能创建模板Objective-C 类):

template <class Cell>
class Grid : public AbstractGrid
{
public:
    Cell **cells;
    virtual someMethod() {}
}

现在我想创建一个ViewBase 的子类,它的类型为myLayer 也是LayerBase 的子类(+myLayerClass 方法也被重新定义)并为模型类使用不同的模板参数,例如:

@interface AView : ViewBase
@property (readonly) ALayer *myLayer;
@end

@interface ALayer : LayerBase
@property Grid<GridCell> *grid;
@end

class GridCell
{
public:
    int row, column;
}

应用程序使用这种方法可以正常工作,但编译器会警告我关于不兼容的属性类型:

属性类型“ALayer *”与继承自“ViewBase”的类型“LayerBase *”不兼容
属性类型“Grid *”与继承自“LayerBase”的类型“AbstractGrid *”不兼容

虽然我可以通过使用id 类型声明层属性来消除第一个警告(这不是最好的解决方案,因为我不能在没有类型转换的情况下使用点语法,而且我可能会犯错误哪个编译器不会能抓到):

@property (readonly) id myLayer;

我不能对 C++ 类型做同样的事情。将grid 属性声明为void * 也无济于事。

那么有没有合适的方法来处理这种情况?或者我应该简单地使用 pragma 使警告静音,因为我知道自己在做什么?

请不要建议不要使用 C++ 类,因为它不是一个选项(我正在创建一组跨平台模型类以方便将来移植)。

【问题讨论】:

标签: c++ ios objective-c templates inheritance


【解决方案1】:

是的。不要更改返回类型。例如:

@interface LayerBase : CALayer
- (AbstractGrid *)grid;
@end


@interface ALayer : LayerBase

// ALayer's local storage and typed interface:
@property Grid<GridCell>* grid_GridCell; // << use unique selector names

// ALayer's abstract interface/overrides:
- (AbstractGrid *)grid; // << returns self.grid_GridCell

@end

【讨论】:

  • 这意味着创建'假' ivars,这对我来说似乎相当愚蠢......而且,它会破坏子类的统一网格访问。目标是让编译器在编译时知道grid 属性的真实类。
  • @kambala 如果您只需要AbstractGrid 的接口,您可以公开AbstractGrid 并使用工厂——无需创建objc 变体。这与 C++ 没有什么不同;出于充分的理由,两种语言的向下转换都需要两种语言的转换。如果您需要静态类型信息,那么无论如何您都应该使用正确类型的变量,并且您的 LayerBase 不会保存 ivar。当然,您也可以使用dynamic_cast&lt;&gt; 动态地完成它。在 objc 中,您不应像示例中那样更改返回类型,原因与 C++ 禁止它的原因相同。
  • Factory 不适合我的情况,因为我为来自外部模型对象的每个 LayerBase 子类设置了 grid 指针。
  • @kambala 我理解你的目标,但是 objc 和 c++ 的语言设计者不希望你有隐式向下转换是有原因的,以及为什么有问题的函数具有无与伦比的签名。如果您想要此类型信息,我建议您保留它并使用模板(不在 objc 接口中),或 dynamic_cast&lt;&gt;AbstractGrid。您的回答需要注意的是编译器没有所有信息;它只够编译程序……
  • @kambala ...这意味着您将需要始终在调用点引入确切的类型信息以处理类型,并且您的类层次结构越深就越困难。这成为一个问题,因为编译器使用您传递的变量的方法的返回类型。如果 objc 类型与声明的类型不同,您将持有指向错误类型的指针。这也会导致返回类型不明确。编译器将来可能会意识到你在做什么并把它叫出来——随着你的层次结构的增长,或者当你在一个 TU 中包含其他 LayerBase 子类型时。 (反
【解决方案2】:

好吧,我决定删除 grid @property(因为在这种情况下我不需要像 KVO 这样的任何属性特性),转而使用旧的 getter/setter,并在子类中简单地转换返回类型。 clang 足够聪明,允许任意 getter 的点语法,所以现在我没有收到任何警告。

@interface LayerBase : CALayer {
    AbstractGrid *_grid;
}
- (AbstractGrid *)grid;
- (void)setGrid:(AbstractGrid *)grid;
@end

@implementation LayerBase
- (AbstractGrid *)grid {
    return _grid;
}

- (void)setGrid:(AbstractGrid *)grid {
    _grid = grid;
}
@end

@interface ALayer : LayerBase
- (Grid<GridCell> *)grid;
@end

@implementation ALayer
- (Grid<GridCell> *)grid {
    return (Grid<GridCell> *)[super grid];
}
@end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多