0

我有三个类的问题。第一类称为Player。这个类NSMutableArray里面有一个叫做units. 该数组由类的对象组成Unit。反过来,这个类有一个NSMutableArray被调用的bullets. 它是这样工作的:

在某个时刻,该类Player(它可能只是ViewController相反)将一个对象添加到units. 然后,当初始化 的实例时Unit,由于上述原因,它会创建一个 NSTimer 负责每秒创建子弹。

问题是,在这中间的某个地方崩溃了SIGABRT,告诉我有一个例外,因为:Collection <__NSArrayM: 0xb1a2970> was mutated while being enumerated.另外,我拿走了产生子弹的线,它停止崩溃,证明这是问题所在。这意味着什么!

这是一些可能有效的可执行代码:

ViewController.h(而不是播放器)

@interface ViewController : UIViewController
{
    NSMutableArray *units;
    NSTimer *updateTimer;
}
-(void)Update;

视图控制器.m

@implementation ViewController
//methods...

- (void)viewDidLoad
{
    //more default code

    //Initialized array and adds one object with the default constructor for simplicity
    units = [[NSMutableArray alloc] initWithObjects:[[Unit alloc] init], nil]
}

-(void)Update
{
    for(Unit *unit in units)
    {
        [unit Update];
        if(unit.deleteFromList)
            [units removeObject:unit];   
    }
}
//More methods
@end

单位.h

@interface Unit : NSObject
{
    NSMutableArray *bullets;
    NSTimer *bulletTimer;
    boolean deleteFromList;
}

@property(readonly, assign)deleteFromList;

-(void)Fire;

-(void)Update;

单位.m

@implementation Unit

@synthesize deleteFromList;

-(id)init
{
    if(self)
    {
        bullets = [[NSMutableArray alloc] init];
        bulletTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(fire) userInfo:NULL repeats:true];
        deleteFromList = false;
    }

    return self;
}

-(void)Fire
{
    [bullets addObject:[[Bullet alloc] init]];
}

-(void)Update
{
    for(Bullet *bullet in bullets)
    {
        [bullet Update];
        if(bullet.deleteFromList)
            [bullets removeObject:bullet];
    }

    if(certainCondition)
        deleteFromList = true;
}

项目符号类将被省略,因为内容与发生的事情无关。此外,所有的类和构造函数都被缩短了,因为其余的对于这个例子是无用的

编辑:

我忘记添加的另一件事是,计时器是在我将要添加的更新方法中的 NSMutableArray 单元的枚举中创建的。我还向 Unit 和 Bullet 添加了一个变量,命令它删除。子弹更新改变了位置,也改变了 deleteFromList 变量

4

3 回答 3

4

您不能在NSMutableArrayfor 循环或枚举时删除任何项目。

文档:在枚举可变集合时修改它是不安全的。某些枚举器当前可能允许枚举已修改的集合,但不保证将来会支持此行为。

for(Bullet *bullet in bullets)
{
    [bullet Update];
    if(bullet.deleteFromList)
        [bullets removeObject:bullet];
}

NSMutableArray *toRemove = [NSMutableArray array];
for(Bullet *bullet in bullets)
{
    [bullet Update];
    if(bullet.deleteFromList)
        [toRemove addObject:bullet];
}
[bullets removeObjectsInArray:toRemove];
于 2012-05-30T02:08:21.080 回答
4

有两种简单的方法 - 一种是创建要删除的对象数组,另一种是迭代原始方式的副本。第二种技术可能更容易阅读。

方法一

NSMutableArray *toRemove = [NSMutableArray array];
for (Bullet *bullet in bullets)
{
    [bullet Update];
    if (bullet.deleteFromList)
    {
        [toRemove addObject:bullet];
    }
}
[bullets removeObjectsInArray:toRemove];

方法二

for (Bullet *bullet in [bullets copy])
{
    [bullet Update];
    if (bullet.deleteFromList)
    {
        [bullets removeObject:bullet];
    }
}

第一种方法稍微冗长一些,但不会复制原始数组。如果原始数组非常大,并且出于性能/内存原因不想复制它,第一种方法是最好的。如果没关系(99% 的时间),我更喜欢第二种方法。


顺便说一句,您不应该在 Objective-C 中以大写字母开头方法名称,除非方法名称以缩写开头,例如URL,因此[bullet Update]应该真正重命名为[bullet update].

于 2012-05-30T02:41:03.093 回答
0

或者

NSIndexSet *indexSet = [units indexesOfObjectsPassingTest:^BOOL(Unit *udit, NSUInteger idx, BOOL *stop) {
    [unit Update];
    return unit.deleteFromList;
}];

[units removeObjectsAtIndexes:indexSet];
于 2012-05-30T02:18:29.703 回答