Search code examples
iosinstrumentsmemory-leaks

Class with memory leaks in instruments


I have a couple of classes which functions are execute SQL statements against a WEB service in order to get or set data in the database.

Works fine, but the problem is when I'm testing in instruments/leaks the 90% of the leaks are because these classes.

Could you tell me what I'm loosing?

Thanks.

Here is the code:

Where the data is stored:

.h

@interface iSQLResult : NSObject {
    NSMutableArray *Records;
}
@property (nonatomic, assign) int CountX;
@property (nonatomic, assign) int CountY;
@property (nonatomic, retain) NSMutableArray *Columns;
@property (nonatomic, retain) NSMutableArray *Records;
@property (nonatomic, retain) NSMutableArray *FieldsNames;
@property (nonatomic, assign) int ErrorCode;
@property (nonatomic, retain) NSString *ErrorDescription;


-(void)addField:(NSString*)fieldName;
-(void)addRecord:(NSMutableArray*)items;
-(NSMutableArray *)getRecord:(int)y;
-(NSString*)getValue:(int) x posY:(int) y;
-(NSString*)getValueByName:(NSString *) colName posY:(int) y;
-(void)setValueByName:(NSString *) colName posY:(int) y value:(NSString *)value;
-(id) copyWithZone: (NSZone *) zone;
@end

.m

#import "iSQLResult.h"
#import <stdarg.h>

@implementation iSQLResult
@synthesize CountX;
@synthesize CountY;
@synthesize Columns;
@synthesize Records;
@synthesize FieldsNames;
@synthesize ErrorCode;
@synthesize ErrorDescription;

-(id)init
{
    self = [super init];

    if (self)
    {
        self.CountX =0;
        self.CountY =0;
        self.ErrorCode = 0;
        self.ErrorDescription = @"";

        self.FieldsNames = [NSMutableArray array];
        self.Columns = [NSMutableArray array];
        self.Records = [NSMutableArray array];

    }

    return self;
}
-(void)removeRecord:(int)index
{
    [self.Records removeObjectAtIndex:index];
    self.CountY = self.CountY - 1;
}
-(void)addField:(NSString*)fieldName
{
    [self.FieldsNames addObject:[NSString stringWithFormat:@"%@", fieldName]];
    self.CountX = self.CountX +1;
}
-(void)addRecord:(NSMutableArray*)items
{
    [self.Records addObject:items];
    self.CountY = self.CountY +1;
}
-(NSMutableArray *)getRecord:(int)y
{
    return [Records objectAtIndex:y];
}
-(NSString *)getValue:(int) x posY:(int)y
{
    return [[NSString stringWithFormat:@"%@", [[Records objectAtIndex:y] objectAtIndex:x]] copy];
}
-(NSString*)getValueByName:(NSString *) colName posY:(int) y
{
    int a=0;
    for (a=0;a<CountX;a++)
    {
        if ([[colName uppercaseString] isEqualToString:[[FieldsNames objectAtIndex:a] uppercaseString]])
        {
            return [[NSString stringWithFormat:@"%@", [[Records objectAtIndex:y] objectAtIndex:a]] copy];
        }
    }
    return @"";
}
-(void)setValueByName:(NSString *) colName posY:(int) y value:(NSString *)value
{
    int a=0;
    for (a=0;a<CountX;a++)
    {
        if ([[colName uppercaseString] isEqualToString:[[FieldsNames objectAtIndex:a] uppercaseString]])
        {
            [[Records objectAtIndex:y] replaceObjectAtIndex:a withObject:value];
        }
    }

}
-(void)dealloc
{
    [Columns release];
    [Records release];
    [FieldsNames release];
    [ErrorDescription release];

    [super dealloc];
}
-(id) copyWithZone: (NSZone *) zone
{
    iSQLResult *SQLRCopy = [[iSQLResult allocWithZone: zone] init];

    [SQLRCopy setCountX:self.CountX];
    [SQLRCopy setCountY:self.CountY];
    [SQLRCopy setRecords:[self.Records mutableCopyWithZone:zone]];
    [SQLRCopy setColumns:[self.Columns mutableCopyWithZone:zone]];
    [SQLRCopy setFieldsNames:[self.FieldsNames mutableCopyWithZone:zone]];
    [SQLRCopy setErrorCode:self.ErrorCode];
    [SQLRCopy setErrorDescription:[self.ErrorDescription copyWithZone:zone]];

    return SQLRCopy;
}
@end

The class who ask for the data to the web service comunication class and gets a xml structure:

.h

#import <Foundation/Foundation.h>
#import "iSQLResult.h"
#import "IM2_WebServiceComm.h"

@interface iSQL : NSObject <NSXMLParserDelegate> {

    iSQLResult *SQLR;
    IM2_WebServiceComm * WSC;

    NSXMLParser *xmlParser;
    BOOL Found;
    BOOL FieldsReaded;
    BOOL loadFieldsNow;
    BOOL loadErrNumNow;
    BOOL loadErrDesNow;
    NSString *chunksString;
    NSMutableArray *tempRecord;
}
@property (nonatomic, retain) NSString *URLConnection;
-(void)SQLReader:(NSString*)SQLString;
-(void)SQLExec:(NSString*)SQLString;
-(void)setURLConnection:(NSString *) WebSURL;
-(iSQLResult*) getSQLR;
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
-(void) parser:(NSXMLParser *) parser foundCharacters:(NSString *)string;
-(void) parser:(NSXMLParser *) parser didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qName attributes:(NSDictionary *) attributeDict;
@end

.m

#import "iSQL.h"
@implementation iSQL
@synthesize URLConnection;
- (iSQLResult*)getSQLR
{
    return [SQLR copy];
}
-(void)SQLExec:(NSString*)SQLString
{

    FieldsReaded = NO;
    Found = NO;
    loadFieldsNow = NO;

    if (SQLR)
    {
        [SQLR release];
        SQLR = nil;
    }


SQLR = [[iSQLResult alloc] init];

WSC = [[IM2_WebServiceComm alloc] init];
[WSC setURL:URLConnection];

NSString *theXML = [WSC callMethod:@"ExecNonQuery" sendInstruction:SQLString];

@try
{
    xmlParser = [[NSXMLParser alloc] initWithData:[theXML dataUsingEncoding:NSUTF8StringEncoding]];
    [xmlParser setDelegate: self];
    [xmlParser setShouldResolveExternalEntities:NO];
    if(![xmlParser parse])
    {
        NSLog(@"ERROR PARSING");
    }
    [xmlParser release];
}
@catch(NSException * ex) 
{
    NSLog(@"%@",[[ex reason] UTF8String]);
}
[WSC release];
}
-(void)SQLReader:(NSString*)SQLString
{

FieldsReaded = NO;
Found = NO;
loadFieldsNow = NO;

if (SQLR)
{
    [SQLR release];
    SQLR = nil;
}

SQLR = [[iSQLResult alloc] init];

WSC = [[IM2_WebServiceComm alloc] init];
[WSC setURL:URLConnection];

NSString *theXML = [WSC callMethod:@"ExecSQL" sendInstruction:SQLString];

@try
{
    xmlParser = [[NSXMLParser alloc] initWithData:[theXML dataUsingEncoding:NSUTF8StringEncoding]];
    [xmlParser setDelegate: self];
    [xmlParser setShouldResolveExternalEntities:NO];
    if(![xmlParser parse])
    {
        NSLog(@"ERROR PARSING");
    }
    [xmlParser release];
}
@catch(NSException * ex) 
{
    NSLog([NSString stringWithFormat:@"%@",[[ex reason] UTF8String]]);
}
[WSC release];
}

-(iSQLResult *)getSingleSQLR:(iSQLResult *)SQLSource usingRow:(int)y
{

iSQLResult *SQLRAux = [[[iSQLResult alloc]init]retain];

[SQLRAux setCountX:SQLSource.CountX];
[SQLRAux addRecord:[SQLSource getRecord:y]];
[SQLRAux setFieldsNames:SQLSource.FieldsNames];

return SQLRAux;

}

-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
    NSLog(@"Error on XML Parse: %@", [parseError localizedDescription] );
}

//#pragma XML Parser Delegate Methods
-(void) parser:(NSXMLParser *) parser didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qName attributes:(NSDictionary *) attributeDict 
{
    if (!chunksString)
{
    chunksString = [[NSString alloc] init];
}
chunksString = @"";
if ([elementName isEqualToString:@"ErrCode"])
{
    loadErrNumNow = YES;
}
if ([elementName isEqualToString:@"ErrDesc"])
{
    loadErrDesNow = YES;
}

if ([elementName isEqualToString:@"Table"])
{
    Found = YES;
    loadFieldsNow = NO;
    tempRecord = [[NSMutableArray alloc] init];
}

if (Found && ![elementName isEqualToString:@"Table"]) 
{
    loadFieldsNow = YES;
    if (!FieldsReaded)
    {
        [SQLR addField:elementName];
    }   
}
}

-(void) parser:(NSXMLParser *) parser foundCharacters:(NSString *)string
{
    chunksString = [chunksString stringByAppendingString:string]; 
}       

-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName 
{
NSString * finalString;
finalString = [NSString stringWithFormat:@"%@",chunksString];

if (loadErrNumNow)
{
    [SQLR setErrorCode:[finalString intValue] ];
    loadErrNumNow = NO;
}
if (loadErrDesNow)
{
    [SQLR setErrorDescription:finalString];
    loadErrDesNow = NO;
}           

if (Found)
{
    if (loadFieldsNow) 
    {
        [tempRecord addObject:finalString]; 
    }
}

if ([elementName isEqualToString:@"Table"])
{
    [SQLR addRecord:tempRecord];
    [tempRecord release];
    Found = NO;
    FieldsReaded = YES;
    loadFieldsNow = NO;

}
}

-(void)dealloc
{
    if (SQLR)
    { 
        [SQLR release];
        SQLR = nil;
    }

    [URLConnection release];

    [super dealloc];

}
@end

This is an absolutely buggy class because the leaks, every access is a leak :(

Any help please?


Solution

  • Here are some things I notice:

    -[iSQLResult copyWithZone:]

    You're sending the result of mutableCopyWithZone: (which returns a retained object) to the setters, which retain the object again. One way to fix it is to autorelease the copy:

    [SQLRCopy setRecords:[[self.Records mutableCopyWithZone:zone] autorelease]]
    

    -[iSQL SQLExec:] and -[iSQL SQLReader:]

    The ivars WSC and xmlPareser are alloced then released but not set to nil. This isn't a leak, but you're keeping a reference to a released object. If you dereference that, you'll crash.

    -[iSQL getSingleSQLR:usingRow:]

    iSQLResult *SQLRAux = [[[iSQLResult alloc]init]retain];
    

    Did you mean autorelease instead of retain there? (And if you did, shouldn't you also autorelease the return in getSQLR, for consistency?)

    -[iSQL parser:didStartElement:namespaceURI:qualifiedName:attributes:]

    chunksString is alloced (+1 retain), but later in parser:foundCharacters: you're losing the reference (i.e., leaking the string) and replacing it with an autoreleased string.


    That's all I notice off hand. It sounds like you're leaking more than just those objects, so I'm guessing you're leaking iSQL and/or iSQLResult objects, too. Remember, when you leak an object, you also leak everything those objects hold a reference to. Even if these classes were leak-free, if the code that's instantiating the classes isn't properly releasing the objects you'll see all the members in these classes leak, too. When fixing leaks, always look for "top-level" objects first.