Memory leak in SChartGL::Drawer::addPieSlice


#1

Hi Guys,

Firstly, let me say - great product!

Currently however we’ve a major issue which results in an app crashing, which is due to go for UAT tomorrow. After opening a particular view controller 3-4 times the app crashes due to a memory leak. Can somebody please help?

The view which crashes has 5 charts (4 pie charts and a bar chart) and it’s leaking memory (screenshot attached).

The code for loading the data is called in a background thread and the charts are built when the data for each is loaded locally on the main thread using a block. So on viewDidLoad I’ve got this notification center observer registration:

// Load the data and build the reports once the data has finished loading.

    [[NSNotificationCenterdefaultCenter] addObserverForName:@"ChartDataLoaded"object:nilqueue:[NSOperationQueuemainQueue] usingBlock:^(NSNotification *note) {

        @try {

            

            self.chartsReturned += 1;

            

            int tag = [((NSNumber*)note.object) intValue];

            

            switch (tag) {

                case 0:

                    [self buildPossessionReport];

                    break;

                case 1:

                    [self buildTrimesterReport:1];

                    break;

                case 2:

                    [self buildTrimesterReport:2];

                    break;

                case 3:

                    [self buildTrimesterReport:3];

                    break;

                case 4:

                    [self buildEngagementReport];

                    break;

                default:

                    break;

            }

        }

        @catch (NSException *exception) {

            NSLog(@"%@", exception);

        }

        @finally {

            if (self.chartsReturned == 5)

            {

                [[NSNotificationCenterdefaultCenter] removeObserver:self];

            }

        }

    }];

And then my viewDidAppear is as follows - I’ve omitted loadData for this because all it’s doing is filling array and dictionary properties on the viewcontroller and there’s no leaks there:

-(void)viewDidAppear:(BOOL)animated

{

    [super viewDidAppear:animated];

    [selfperformSelectorInBackground:@selector(loadData) withObject:nil];

}

Finally all datasource code can be seen below. 

#pragma mark - SChartDatasource

-(int)numberOfSeriesInSChart:(ShinobiChart *)chart

{

    if (chart.tag < 4) return 1;

    

    return 1;

}

-(SChartSeries *)sChart:(ShinobiChart *)chart seriesAtIndex:(int)index

{

    if (chart.tag < 4) { // One of the possesion charts.

        

        // Get the number of data points for the series.

        int dataPointCount = [self sChart:chart numberOfDataPointsForSeriesAtIndex:index];

        

        SChartPieSeries * pieSeries = [[SChartPieSeries alloc] init];

        pieSeries.selectionAnimation.duration = @0.4;

        pieSeries.selectedStyle.showCrust = NO;

        pieSeries.style.showCrust = NO;

        pieSeries.selectedPosition = @0.0;

        pieSeries.style.showFlavour = YES;

        [pieSeries.style.flavourColorssetObject:APP_BLUE_COLORatIndexedSubscript:0];

        [pieSeries.style.flavourColorssetObject:APP_RED_COLORatIndexedSubscript:0];

        [pieSeries.selectedStyle.flavourColorssetObject:APP_BLUE_COLORatIndexedSubscript:0];

        [pieSeries.selectedStyle.flavourColorssetObject:APP_RED_COLORatIndexedSubscript:0];

        

        if (dataPointCount == 1 && chart.tag < 4)

        {

            // We only should be showing student or teacher. Change the flavour colours.

            NSArray * keys = [self.dsTotalPossessions allKeys];

            if (chart.tag == 3) keys = [self.dsPossessionTrimester3 allKeys];

            if (chart.tag == 2) keys = [self.dsPossessionTrimester2 allKeys];

            if (chart.tag == 1) keys = [self.dsPossessionTrimester1 allKeys];

            

            if ([NSLocalizedString(@“Teacher”, @“Teacher”) isEqualToString:[keys objectAtIndex:0]])

            {

                [pieSeries.style.flavourColors setObject:APP_RED_COLOR atIndexedSubscript:0];

                [pieSeries.selectedStyle.flavourColorssetObject:APP_RED_COLORatIndexedSubscript:0];

            }

            else

            {

                [pieSeries.style.flavourColors setObject:APP_BLUE_COLOR atIndexedSubscript:0];

                [pieSeries.selectedStyle.flavourColorssetObject:APP_BLUE_COLORatIndexedSubscript:0];

            }

        }

        

        return pieSeries;

    }

    

    SChartBarSeries * barSeries = [[SChartBarSeriesalloc] init];

    

    return barSeries;

}

-(int)sChart:(ShinobiChart *)chart numberOfDataPointsForSeriesAtIndex:(int)seriesIndex

{

    if (chart.tag < 4)

    {

        if (chart.tag == 3) return [[self.dsPossessionTrimester3 allKeys] count];

        if (chart.tag == 2) return [[self.dsPossessionTrimester2 allKeys] count];

        if (chart.tag == 1) return [[self.dsPossessionTrimester1 allKeys] count];

        return [[self.dsTotalPossessionsallKeys] count];

    }

    

    return11; // engagement charts.

}

-(id<SChartData>)sChart:(ShinobiChart *)chart dataPointAtIndex:(int)dataIndex forSeriesAtIndex:(int)seriesIndex

{

    if (chart.tag < 4)

    {

        NSDictionary * targetDictionary = self.dsTotalPossessions;

        if (chart.tag == 1) targetDictionary = self.dsPossessionTrimester1;

        if (chart.tag == 2) targetDictionary = self.dsPossessionTrimester2;

        if (chart.tag == 3) targetDictionary = self.dsPossessionTrimester3;

        

        SChartRadialDataPoint *datapoint = [[SChartRadialDataPointalloc] init];

        NSString* key = [[targetDictionary allKeys] objectAtIndex:dataIndex];

        datapoint.name = key;

        

        NSArray * values = [targetDictionary objectForKey:key];

        

        datapoint.value =[NSNumber numberWithInt:[values count]];

        

        targetDictionary = nil;

        

        return datapoint;

    }

    else

    {

        double value = [[self.dsEngagement objectAtIndex:dataIndex] doubleValue];

        

        SChartDataPoint *datapoint = [[SChartDataPointalloc] init];

        

        NSString* key = [NSString stringWithFormat:@"%i", dataIndex];

        datapoint.xValue = [NSNumbernumberWithDouble:round(value)]; //[self DoubleToNSStringTime:value];

        datapoint.yValue = key;

        

        return datapoint;

    }

}

The following instruments screenshot is the result of popping into the view several times. Can somebody please help!


#2

Hi GotFocus,

It’s great your enjoing using ShinobiCharts, we really appreciate any feedback you have!

As for your memory leak, I can’t see any problems just from looking at the code you provided. Would you be able to send in a cut down version of your project demonstrating just the issue to info@shinobicontrols.com? Failing that, the project itself? I understand that it can be difficult to cut down a large project, but this would really help us get to the root of your problem :grin:.

Thanks,
Jan


#3

Jan helped me solve this problem. It was in fact nothing at all to do wit Shinobi controls, it was to do with NSNotificationCenter retaining the view. Thanks guys.