【问题标题】:How to write my own iterator for a collection property of a property (with correct type casting)?如何为属性的集合属性编写我自己的迭代器(使用正确的类型转换)?
【发布时间】:2012-09-23 04:11:46
【问题描述】:

我有一个包含一些对象组合的模型类,但我不知道为此编写迭代器的最佳方法。要更详细地查看问题,这里是层次结构(半伪代码):

一个根类:

MYEntity : NSObject
@property int commonProperty;
@property NSArray *childs; //Childs of any kind.

一些具体的子类:

MYConcreteStuff : MYEntity
@property int number;

MYConcreteThing : MYEntity
@property NSString *string;

还有一个带有具体集合的根对象:

MYRoot : MYEntity
@property MYEntity *stuff; //Collect only stuff childs here.
@property MYEntity *things; //Collect only thing childs here.

现在我可以为集合(在 MYEntity 中)编写很酷的成员访问器,例如:

-(MYEntity*)entityForIndex:(int) index
{
    if ([self.childs count] > index)
        return [self.childs objectAtIndex:index];

    return nil;      
}

还有更酷的、用于根对象的类型转换成员访问器。

-(MYConcreteThing*)thingForIndex:(int) index
{
    if ([self.things count] > index)
        return (MYConcreteThing*)[self.things entityForIndex];

    return nil;    
}

但我不知道如何为此类集合编写一些单行迭代器。 想要的客户端代码类似于:

for (MYConcreteThing *eachThing in myRoot.things)
  eachThing.string = @"Success."; //Set "thingy" stuff thanks to the correct type.

我正在考虑使用积木,但可能会有更简洁的解决方案。 有什么想法/经验吗?

【问题讨论】:

    标签: objective-c ios collections casting iterator


    【解决方案1】:

    我现在将继续讨论块,这很简单。现在我更喜欢枚举这个词。

    事物枚举的块类型(确保类型正确):

    typedef void (^MYThingEnumeratorBlock)(MYThing *eachThing);
    

    一个很酷的 MYRoot 中事物的枚举器方法(不暴露集合):

    -(void)enumerateThings:(MYThingEnumeratorBlock) block
    {    
        for (MYThing *eachThing in self.things.childs)
            block(eachThing);
    }
    

    所以客户端代码去:

    [myRoot enumerateThings:^(MYThing *eachThing)
     {
        NSLog(@"Thing: %@", eachThing.string);         
     }];
    

    加上一些简洁的宏:

    #define ENUMARATE_THINGS myRoot enumerateThings:^(MYThing *eachThing)
    
    [ENUMARATE_THINGS
    {
       NSLog(@"Thing: %@", eachThing.string); //Cool "thingy" properties.        
    }];
    

    【讨论】:

      【解决方案2】:

      在我看来,最好的方法是为数组属性实现符合键值编码的方法。这将有额外的好处,使您的集合可以被其他对象观察到。您可以在apple documentation 中阅读所有相关信息。这是 MYRoot 类中 Things 数组的示例实现。随意个性化每种方法中的代码:

      // KVC method for read-only array of Things
      - (NSUInteger) countOfThings
      {
          return _things.count;
      }
      
      - (Thing*) objectInThingsAtIndex:(NSUInteger)index
      {
          return [_things objectAtIndex:index];
      }
      
      // Additional KVC methods for mutable array collection
      - (void) insertObject:(Thing*)thing inThingsAtIndex:(NSUInteger)index
      {
          [_things insertObject:thing atIndex:index];
      }
      
      - (void) removeObjectInThingsAtIndex:(NSUInteger)index
      {
          [_things removeObjectAtIndex:index];
      }
      

      要遍历集合,您需要执行以下操作:

      for (Thing *thing in [_entity valueForKey:@"things"]) {
      }
      

      要在数组中添加一个东西,你可以这样做

      NSMutableArray *children = [_entity mutableArrayValueForKey:@"things"];
      [children addObject:aThing];
      

      这样做可以确保所有观察@"things" 属性的对象都会收到有关数组所有更改的通知。如果您直接调用插入方法,它们不会(这有时本身很有用)。

      【讨论】:

      • 如果 things 是一个数组,那么它可以简单地通过 for (Thing *thing in myRoot.things) 来完成,没有 KVO 的东西。
      • 但事物是一个实体,它有一个名为 childs 的数组。这就是为什么我需要一些间接性。
      • 我将问题修改为更清晰。或者至少是准确的。 :)
      • 这就是重点。在上面的示例代码中,我对 Thing 没有任何假设,但我假设上述方法是在 Entity 或 MyRoot 中实现的,这就是为什么您可以编写类似 for (Thing *thing in [my_entity valueForKey:@"things"] )。
      • 我明白了。因此,使用 KVO 方法指向 Thing 的孩子而不是事物本身就可以解决问题。我喜欢这样。
      猜你喜欢
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 2011-06-23
      • 2011-06-28
      • 1970-01-01
      相关资源
      最近更新 更多