I've had this problem for a couple of days now, I've googled a lot but found out nothing useful so I thought to ask here. I've got a retention problem with an NSMutableArray, however I can't understand where the trouble is and how to fix it. I am drawing a a line chart using Quartz 2D, and in order to do so I have subclassed UIView with GraphView. The header looks like this:
@interface GraphView : UIView {
NSMutableArray *data;
}
- (void)drawBar:(CGRect)rect context:(CGContextRef)ctx;
- (void)drawLineGraphWithContext:(CGContextRef)ctx;
- (void)addValue:(float)value;
@property(nonatomic, retain) NSMutableArray *data;
@end
I use the "data" array to store data points to draw, within the drawLineGraphWithContext: method. The addValue: method, on the other hand, is used in the main controller to fill in the data array. In the viewDidLoad of the controller I do, to fill in the data array:
graphView = [[GraphView alloc] init];
float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};
for(int i = 0; i < 14; i++)
{
[graphView addValue:data[i]];
}
Everything is good at this point, and in debug I can see the array is populated with all elements. The problem is when the drawLineGraphWithContext: method is invoked; for some reason, the array suddenly gets empty and its retain count is 0. I have tried changing the way I access the object, using dot notation or omitting the self, however the behaviour looks the same. Strangely enough, I have tried identifying zombies (with both GDB and Instruments), however none seems to be found. As a matter of fact, then the main loop within the drawLineGraphWithContext: below is started the data array has zero elements, and nothing is drawn. Below complete files (I am omitting irrelevant parts).
I am using Xcode 4.2 with Automatic Reference Counting.
Thanks in advance to anyone helping!
GraphView.h:
@interface GraphView : UIView {
NSMutableArray *data;
}
- (void)drawBar:(CGRect)rect context:(CGContextRef)ctx;
- (void)drawLineGraphWithContext:(CGContextRef)ctx;
- (void)addValue:(float)value;
@property(nonatomic, retain) NSMutableArray *data;
@end
GraphView.m:
#import "GraphView.h"
@implementation GraphView
@synthesize data;
- (id) init
{
self = [super init];
if(self) {
data = [NSMutableArray array];
NSLog(@"%s", __FUNCTION__);
}
return self;
}
- (void)addValue:(float)value
{
NSMutableArray *tempArr = [self data];
NSNumber *val = [NSNumber numberWithFloat:value];
[tempArr addObject:val];
[self setData:tempArr];
}
- (void)drawLineGraphWithContext:(CGContextRef)ctx
{
CGContextSetLineWidth(ctx, 2.0);
CGContextSetStrokeColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1] CGColor]);
int maxGraphHeight = kGraphHeight - kOffsetY;
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * [[[self data] objectAtIndex:0] floatValue]);
int i = 0;
NSMutableArray *arr = [self data];
for(NSNumber *elem in arr)
{
float val = [elem floatValue];
CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * val);
i++;
}
CGContextDrawPath(ctx, kCGPathStroke);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.6);
CGContextSetStrokeColorWithColor(context, [[UIColor lightGrayColor] CGColor]);
CGFloat dash[] = {2.0, 2.0};
CGContextSetLineDash(context, 0.0, dash, 2);
int lines = (kDefaultGraphWidth - kOffsetX)/kStepX;
for(int i = 0; i < lines; i++)
{
CGContextMoveToPoint(context, kOffsetX + i * kStepX, kGraphTop);
CGContextAddLineToPoint(context, kOffsetX + i * kStepX, kGraphBottom);
}
int horizontalLines = (kDefaultGraphWidth-kOffsetY)/kStepY;
for(int i = 0; i < horizontalLines; i++)
{
CGContextMoveToPoint(context, kOffsetX, kGraphBottom - kOffsetY - i * kStepY);
CGContextAddLineToPoint(context, kDefaultGraphWidth, kGraphBottom - kOffsetY -i * kStepY);
}
CGContextStrokePath(context);
CGContextSetLineDash(context, 0, NULL, 0);
[self drawLineGraphWithContext:context];
}
@end
GRViewController.m:
#import "GRViewController.h"
@implementation GRViewController
@synthesize scroller;
@synthesize graphView;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
scroller.contentSize = CGSizeMake(kDefaultGraphWidth, kGraphHeight);
graphView = [[GraphView alloc] init];
float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};
for(int i = 0; i < 14; i++)
{
[graphView addValue:data[i]];
}
}
@end
GRViewController.h:
#import <UIKit/UIKit.h>
#import "GraphView.h"
@interface GRViewController : UIViewController
@property (retain, nonatomic) IBOutlet UIScrollView *scroller;
@property (retain, nonatomic) IBOutlet GraphView *graphView;
@end