仪器中存在内存泄漏的类

Posted

技术标签:

【中文标题】仪器中存在内存泄漏的类【英文标题】:Class with memory leaks in instruments 【发布时间】:2012-02-29 21:47:44 【问题描述】:

我有几个类,它们的功能是针对 WEB 服务执行 SQL 语句,以获取或设置数据库中的数据。

工作正常,但问题是当我在仪器中测试/泄漏时,90% 的泄漏是因为这些类。

你能告诉我我失去了什么吗?

谢谢。

代码如下:

数据的存储位置:

.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

向Web服务通信类请求数据并获取xml结构的类:

.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

这是一个绝对有问题的类,因为泄漏,每次访问都是泄漏:(

有什么帮助吗?

【问题讨论】:

哇。要求互联网上随机的无偿人员查看内存管理错误需要大量代码。如果您需要帮助,请将其缩小为一小部分和特定错误。 您似乎从未在 SQLExec 中释放分配给 SQLR 的第二个值。 (请学习标准的 C 大写约定,以使您的代码更容易被其他人遵循。) 【参考方案1】:

以下是我注意到的一些事情:

-[iSQLResult copyWithZone:]

您将 mutableCopyWithZone: (返回一个保留对象)的结果发送给设置器,后者再次保留该对象。修复它的一种方法是自动释放副本:

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

-[iSQL SQLExec:] 和 -[iSQL SQLReader:]

ivars WSC 和 xmlPareser 被分配然后释放,但不设置为 nil。这不是泄漏,但您保留了对已释放对象的引用。如果你取消引用它,你会崩溃。

-[iSQL getSingleSQLR:usingRow:]

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

您的意思是自动释放而不是保留在那里吗? (如果你这样做了,你不应该也自动释放 getSQLR 中的返回,以保持一致性吗?)

-[iSQL 解析器:didStartElement:namespaceURI:qualifiedName:attributes:]

chunksString 已分配(+1 保留),但稍后在 parser:foundCharacters: 您丢失了引用(即泄漏字符串)并将其替换为自动释放的字符串。


这就是我注意到的全部。听起来您泄漏的不仅仅是那些对象,所以我猜您也在泄漏 iSQL 和/或 iSQLResult 对象。请记住,当您泄漏一个对象时,您也会泄漏这些对象所引用的所有内容。即使这些类是无泄漏的,如果实例化类的代码没有正确释放对象,您也会看到这些类中的所有成员泄漏。修复泄漏时,始终首先查找“***”对象。

【讨论】:

感谢您的回复。这意味着每次我声明一个iSQLResult对象并将其分配给[iSQL getSQLR]的返回时,签名的对象也必须释放?谢谢。 是的,-[iSQL getSQLR] 中的复制方法返回一个保留计数为 +1 的对象。为避免泄漏,您需要在调用范围完成时释放它或更改代码以返回 [[SQLR copy] autorelease]。

以上是关于仪器中存在内存泄漏的类的主要内容,如果未能解决你的问题,请参考以下文章

重新仪器“内存泄漏”分析,其他工具是不是提供更多信息以找到泄漏的根本原因?

iphone/ipad 内存泄漏仪器工具

内存泄漏仪器警告

使用仪器检查 xcode 4 中的内存泄漏

仪器报告内存泄漏。不明白为啥

分析仪结果与仪器泄漏:iPhone 内存泄漏