【问题标题】:Managing objects of different classes in a single NSMutableArray在单个 NSMutableArray 中管理不同类的对象
【发布时间】:2012-03-16 03:31:25
【问题描述】:

我正在寻求有关这是否是一种好的做法的建议。对于冗长的解释,我深表歉意。我有一个很大的 x,y 坐标网格。网格被不同类的对象占据,所有对象都具有不同的方法和数据。当用户触摸网格坐标时,我正在寻找最简洁的方法将我的程序路由到正确的方法,具体取决于对象类型。

我将使用形状作为一种简单的方式来解释我在做什么。假设我有 2 个类,Circle 和 Square,它们的父类都是 Shape。

我正在将 Circle 类和 Square 类的对象添加到名为 shapeManager 的 NSMutableArray 中。当用户触摸网格时,我想找出该坐标的对象类型,以便我可以路由到适当的方法。

for (Shape *shape in shapeManager) {

  if (shape.type == kCircle) {

    [self circleSelected:shape];

  }

}

-(void)circleSelected:(circle *)circle { }

当我这样做时,我是否将指针重铸为不同的类?这有什么缺点吗?我只是在寻找一种处理网格上触摸事件的好方法,当位于该坐标处的对象未知并且具有多个可能的类时。

【问题讨论】:

    标签: objective-c arrays object pointers


    【解决方案1】:

    如果您添加另一个Shape,您的方法的缺点就会出现 - 您最终不得不更改此代码。另一种方法是向您的Shape 类添加一个方法,例如userSelected,并让每个子类覆盖此方法以执行适合它们所代表的形状类型的任何事情。

    在另一种方法中,您的循环变为:

    for(Shape *shape in shapeManager)
       [shape userSelected];
    

    例如,Circle 获得一个方法:

    @implementation Circle
    
    // override Shape's userSelected and do what a circle needs to do
    - (void) userSelected
    {
       ...
    }
    

    您选择哪种方法取决于您,没有正确答案本身,但面向对象的风格通常倾向于后者。

    评论跟进

    要从子级转换为父级,您不需要强制转换,但可以包含强制转换 - 这是 Obj-C 等基于继承的面向对象语言的基本组成部分。

    反过来,从父到子,在 Obj-C 中 (a) 需要强制转换并且 (b) 应该受到检查保护 - 两者都是因为您不知道您的子代中的哪一个(如果有的话)有。在 Obj-C 中,(a) 只是告诉编译器你认为你知道并且它应该信任你,(b) 你是否正在检查以确保!

    您 (b) 使用 [<obj> isKindOfClass:[<classname> class] 来测试 <obj> 是否属于 <classname> 类型 它的任何子项;或者您可以使用isMemberOfClass 来测试<obj> 是否属于<classname> 类型但不是它的任何子级。前者更常见,以便于扩展(例如,您可以从 Rectangle 开始是 Shape,然后引入 Square 作为 Rectangle;对 isMemberOfClass:[Rectangle class] 的测试将是 YES RectangleSquareisKindOfClass:[Rectangle class] 仅是YES for Rectangle)。

    通常不需要显式测试,因为方法分派通常包含它 - 如上面的[shape userSelected],它将在运行时根据shape 的实际类型调用userSelected 的适当实现。当需要进行显式测试时,使用测试和强制转换就是这样做的方法。

    是的,混合类型的集合很常见,事实上,它们是 Obj-C 等基于继承的面向对象语言的另一个组成部分。

    [注意:Obj-C 缺少许多其他语言所具有的一种方法来限制集合可能包含的内容。例如。 NSMutableArray 可以包含 any 对象,而在您的情况下,能够说 NSMutableArray of Shape 将其限制为 Shape 及其子类可能会有所帮助。 C#、Ada 甚至 C++(尽管它强制执行任何东西)都提供了这一点。如果需要,您可以在 Obj-C 中自己完成,请参阅this answer。]

    【讨论】:

    • 感谢您的 cmets 对一般练习的看法。您能否评论将对象转换为不同的类?我很难找到有关它的任何文档。您是否总是可以通过参数类型简单地将对象来回转换为父/子类?这是管理所有来自同一个父类的不同类的许多对象的常用方法吗?
    【解决方案2】:

    NSObject 派生的所有类都继承一个名为isMemberOfClass: 的方法,该方法将使用自省测试类成员资格。您可能会发现这对您的目的很有用。

    for (Shape *shape in shapeManager) {
    
        if ([shape isMemberOfClass:[Circle class]]) {   
            [self circleSelected:(Circle*)shape];
        }
        else if ([shape isMemberOfClass:[Square class]]} {
            [self squareSelected:(Square*)shape];
        }
    }
    

    还有一个 isKindOfClass: 方法可以进行类似的测试,但如果接收对象是您正在测试的类的子类,也会返回 true。

    【讨论】:

      【解决方案3】:

      如何在Shape 的子类中实现'selected' 方法,即Circle 和Square 类。然后对象可以直接调用'selected'方法。

      for (Shape *shape in shapeManager) {
          [self selected];
      }
      

      圈子实现

      @interface Circle: Shape
      {
      }
      @implementation Circle
      -(void)selected
      {
          // do your circle select stuff here
      }
      
      @end
      

      方形实现

      @interface Square: Shape
      {
      }
      @implementation Square
      -(void)selected
      {
          // do your square select stuff here
      }
      
      @end
      

      【讨论】:

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