jcr
jcr

Reputation: 313

Strange NSRangeException beyond bounds error in NSMutablearray

In my app, I'm doing audio analysis. Every second, I'm calling some methods, like this one :

- (NSNumber *) arrayAverage: (NSMutableArray *)array
{
    int countArray = [array count];

    if(!countArray)
        return nil;

    else if (countArray <= 5 )
        return [array valueForKeyPath:@"@avg.doubleValue"];

    else
    {
        // Création et initialisation d'un tableau temporaire
        NSMutableArray *averageArray = [[NSMutableArray alloc] initWithArray:array];
        NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES];
        [averageArray sortUsingDescriptors:[NSArray arrayWithObject:desc]];


        for (int j = 0; j < countArray / 3; j++)
        {
            [averageArray removeLastObject];
            [averageArray removeObjectAtIndex:0];
        }


        NSNumber * average = [averageArray valueForKeyPath:@"@avg.doubleValue"];


        return average;
        }

}

The problem is that I sometime receive a NSRangeException error (after 1min or a few hours...it depends...) saying that the array index is beyond bounds. The strange thing is that the index is NOT out of bounds... Those methods are only called on the main thread. Thanks in advance for your help !

EDIT 1 : With the help of Anim and Abhinav, I have changed my code as following. It has worked for more than 2hours 45min (which is a record) and then crash with a EXC_BAD_ACCESS code 1 error...

- (NSNumber *) arrayAverage: (NSMutableArray *)array
{
    NSArray *arrayCopy = [[NSArray alloc] initWithArray:array]; // CRASH with EXC_BAD_ACCESS error

    int countArray = [arrayCopy count];

    if(!countArray)
        return nil;

    else if (countArray <= 5 )
        return [arrayCopy valueForKeyPath:@"@avg.doubleValue"];

    else
    {
        // Création et initialisation d'un tableau temporaire
        NSMutableArray *averageArray = [[NSMutableArray alloc] initWithArray:arrayCopy];
        NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES];
        [averageArray sortUsingDescriptors:[NSArray arrayWithObject:desc]];

        int startOfRange = countArray / 3;
        int rangeLength = countArray - 2 * (countArray / 3);

        NSArray* slicedArray = [averageArray subarrayWithRange:NSMakeRange(startOfRange, rangeLength)];

        NSNumber * average = [slicedArray valueForKeyPath:@"@avg.doubleValue"];

        return average;
    }

}

Upvotes: 1

Views: 1275

Answers (2)

PR Singh
PR Singh

Reputation: 653

you can do one more thing, just add one condition before the loop: here is the snippet, have added comment:

- (NSNumber *) arrayAverage: (NSMutableArray *)array
{
    int countArray = [array count];

    if(!countArray)
        return nil;

    else if (countArray <= 5 )
        return [array valueForKeyPath:@"@avg.doubleValue"];

    else
    {
        // Création et initialisation d'un tableau temporaire
        NSMutableArray *averageArray = [[NSMutableArray alloc] initWithArray:array];
        NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES];
        [averageArray sortUsingDescriptors:[NSArray arrayWithObject:desc]];

       if ([array count] > 0)  //Added one condition here---PR Singh
      { 
          for (int j = 0; j < countArray / 3; j++)
          {
             [averageArray removeLastObject];
             [averageArray removeObjectAtIndex:0];
          }
      }


        NSNumber * average = [averageArray valueForKeyPath:@"@avg.doubleValue"];


        return average;
        }

}

Upvotes: 0

Abhinav
Abhinav

Reputation: 38152

With given piece of code it would more of guessing on whats going on here. Please make sure following:

  1. Before calling "removeLastObject" or "removeObjectAtIndex", it makes sense to put a check on the array count. Call them only when there is something to remove.
  2. Given that NSArray and NSMutableArray are not thread safe, make sure that the arrays you are operating on are not getting modified and used at the same time.

I would like to induce following statements inside your For loop:

if (averageArray.count > 0) {
    [averageArray removeLastObject];
}

if (averageArray.count > 0) {
    [averageArray removeObjectAtIndex:0];
}

Upvotes: 2

Related Questions