1

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;
    }

}
jcr
  • 303
  • 1
  • 6
  • 16
  • That "`for`" loop looks highly suspicious to me, but I can't put my finger on it. You should check to make certain that "`averageArray`" has at least two members before removing things. – Michael Dautermann Oct 20 '13 at 16:48
  • @Michael, Thanks for your answer. I have already tried. The app usually crashes when the array is full (180 objects). That is why I really can't understand the problem... – jcr Oct 20 '13 at 17:06
  • btw: -subarrayWithRange: (NSArray) would shorten your code. – Amin Negm-Awad Oct 20 '13 at 18:36
  • @Anim, Thanks for the trick ! I didn't know this code. – jcr Oct 20 '13 at 18:51

2 Answers2

2

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];
}
Abhinav
  • 37,684
  • 43
  • 191
  • 309
  • Thank you Abhinav. How would you modified my code regarding the second issue ? – jcr Oct 20 '13 at 17:10
  • Thanks Abhinav. Actually I was talking about the second part of your answer : how can I be sure that the arrays are not modified and used at the same time ? – jcr Oct 20 '13 at 18:03
  • Oh I see. For that you should not directly operate on instance objects. Try to use copy of NSArray in local variable and use that for looping around. This will make it safe the instance variables from being modified from somewhere else. – Abhinav Oct 20 '13 at 18:09
  • @jcr If this solves your problem then can you please accept this answer. – Abhinav Oct 21 '13 at 01:48
  • @Abhinav, I edited my question. Can you please have a look on it ? – jcr Oct 21 '13 at 08:17
  • @Abhinav, I'm trying to make it crash again ;) seems to be long. – jcr Oct 21 '13 at 19:01
  • @jcr If you were directly running it on device then you can pull the logs from device logs after connecting it with system. Otherwise, yes, it will take another shot. One suggestion, increase the frequency of execution and may be you could replicate it little earlier. – Abhinav Oct 21 '13 at 20:02
  • @Abhinav, I added NSLog(@"%@",[NSThread callStackSymbols]); to my code. Is it what you are waiting for ? Thank you. – jcr Oct 21 '13 at 21:16
  • I found out the problem. As my array comes from the audio processing, it was not safe. That's ok now ! Thanks. – jcr Oct 23 '13 at 05:17
0

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;
        }

}
PR Singh
  • 653
  • 5
  • 15