iPhone SDK 中的 NSMutableArray、NSArray、NSString 内存泄漏

Posted

技术标签:

【中文标题】iPhone SDK 中的 NSMutableArray、NSArray、NSString 内存泄漏【英文标题】:Memory leak in NSMutableArray, NSArray, NSString in iPhone SDK 【发布时间】:2011-04-16 10:57:28 【问题描述】:

在我的应用程序中,我在 NSMutableArray、NSArray 和 NSString 中遇到了内存泄漏。

这里是代码。

    NSString *subQuery = [NSString stringWithFormat:@"SELECT %@ FROM tbl_lang WHERE glossary = '%@'",append1,glossaryName];
    NSArray *subArray1 = [[[self returnExecuteQuery:subQuery] mutableCopy] autorelease];        
    [subArray addObjectsFromArray:subArray1];

    NSString *columnQuery = [NSString stringWithFormat:@"select AutoID,%@ from tbl_lang where glossary='%@'",lblshortName.text,glossaryName];
    NSArray *newArray =[[[self returnExecuteQuery:columnQuery] mutableCopy] autorelease];
    [langArray addObjectsFromArray:newArray];

    NSMutableArray *tempArray = [[NSMutableArray alloc] init];

    for (int i=0; i<[newArray count]; i++) 
        NSString *cellText = [[newArray objectAtIndex:i] valueForKey:[NSString stringWithFormat:@"%@",lblshortName.text]];

        if (cellText != (NSString *)[NSNull null] && ![cellText isEqualToString:@""] ) 
            NSString *decodedString3 = [NSString stringWithUTF8String:[cellText cStringUsingEncoding:[NSString defaultCStringEncoding]]];

            [tempArray addObject:[NSString stringWithFormat:@"%@ : %@",lblshortName.text, decodedString3]];
        
        else 
            [tempArray addObject:@"<empty>"];
        

        NSString *detail = @"_________________";

        for (int j=0; j<[lableNameArray count]; j++) 

            NSString *checkNull=[[subArray1 objectAtIndex:i] valueForKey:[NSString stringWithFormat:@"%@",[lableNameArray objectAtIndex:j]]];

            if(checkNull != (NSString *)[NSNull null] && checkNull.length > 0)
            
                NSString *decodedString4 = [NSString stringWithUTF8String:[checkNull cStringUsingEncoding:[NSString defaultCStringEncoding]]];
                detail = [NSString stringWithFormat:@"%@\n%@ : %@ ",detail,[lableNameArray objectAtIndex:j],decodedString4];
                           
           
        [detailTextArray addObject:detail]; 

    

当我在 Instruments 中运行时,我遇到了泄漏

-subArray1 在第二行。

-第二个for循环中的详细信息(NSString)。

而 subArray 和 langArray 是我的全局数组。

如果我从 NSArray *newArray =[[[self returnExecuteQuery:columnQuery] mutableCopy] autorelease];NSArray *subArray1 = [[[self returnExecuteQuery:subQuery] mutableCopy] autorelease]; 中删除 mutableCopy,则 subArray 和 langArray 不会保留值。

如何避免这段代码中的内存泄漏?

【问题讨论】:

-returnExecuteQuery: 是做什么的?它返回一个拥有或非拥有的对象?它的合同是否说明了多次调用的返回值有效性? 你发布 tempArray 吗?在哪里?此外,尝试在 addObjectsFromArray 之后释放 subArray1 和 newArray(而不是自动释放它们)。 @Fran :我试图在 main for 循环之后释放两个数组,但它给了我 subArray 和 langArray 的 EXEC_BAD_ACCESS 错误。 @Bavarious : returnExecuteQuery 给了我从数据库中提取的 NSArray。 我得到了同样的警告......这是我的截图和question .. 【参考方案1】:

Olease 试试这个,在上面的代码中,您创建了两个属于自动释放池的多个对象,这是我尝试处理这些字符串变量的释放的一个版本。

其次,detail 的泄漏是因为您在代码中多次取消引用它。对于subArray1,请查看评论

    NSMutableString *subQuery =[ [NSMutableString alloc] initWithFormat:@"SELECT %@ FROM tbl_lang WHERE glossary = '%@'",append1,glossaryName];
   // please make returnExecuteQuery's returned array autorelease if it is not.  
   NSArray *subArray1 = [[self returnExecuteQuery:subQuery] mutableCopy] ;
    [subArray addObjectsFromArray:subArray1];

    [subQuery release]; 
    NSMutableString *columnQuery ==[ [NSMutableString alloc] initWithFormat:@"select AutoID,%@ from tbl_lang where glossary='%@'",lblshortName.text,glossaryName];
    NSArray *newArray =[[self returnExecuteQuery:columnQuery] mutableCopy] ;
    [langArray addObjectsFromArray:newArray];
    [columnQuery relese];
    NSMutableArray *tempArray = [[NSMutableArray alloc] init];

    for (int i=0; i<[newArray count]; i++) 
        NSMutableString *tempKey = [[NSMutableString  alloc]initWithFormat:@"%@",lblshortName.text]];


        NSString *cellText = [[newArray objectAtIndex:i] valueForKey:tempKey];
        [tempKey release];

        if (cellText != (NSString *)[NSNull null] && ![cellText isEqualToString:@""] ) 
            NSString *decodedString3 = [NSString stringWithUTF8String:[cellText cStringUsingEncoding:[NSString defaultCStringEncoding]]];
            NSMutableString *tempString  = [[NSMutableString alloc] initWithFormat:@"%@ : %@",lblshortName.text, decodedString3]];

            [tempArray addObject:tempString];
            [tempString release];
        
        else 
            [tempArray addObject:@"<empty>"];
        

        NSMutableString *detail = nil;

        for (int j=0; j<[lableNameArray count]; j++) 
        
             detail = [[ NSMutableString alloc]initWithString:@"_________________"];
            NSMutableString *key = [[NSMutableString alloc]initWithFormat:@"%@",[lableNameArray objectAtIndex:j]];
            NSString *checkNull=[[subArray1 objectAtIndex:i] valueForKey:key];
           [key release];

            if(checkNull != (NSString *)[NSNull null] && checkNull.length > 0)
            
                NSString *decodedString4 = [NSString stringWithUTF8String:[checkNull cStringUsingEncoding:[NSString defaultCStringEncoding]]];
                [detail setString:[NSString stringWithFormat:@"%@\n%@ : %@ ",detail,[lableNameArray objectAtIndex:j],decodedString4]];
             
              [detailTextArray addObject:detail]; 
              [detail release];              
           


    
    [subArray1 release];
    [newArray release];

UPDATE : 请务必阅读代码中的 cmets 并回复,以便改进。

    NSMutableString *subQuery =[ [NSMutableString alloc] initWithFormat:@"SELECT %@ FROM tbl_lang WHERE glossary = '%@'",append1,glossaryName];
   //*****NOTE THIS POINT ----> please make returnExecuteQuery's returned array autorelease if it is not.  
   NSArray *subArray1 = [[self returnExecuteQuery:subQuery] mutableCopy] ;
    [subArray addObjectsFromArray:subArray1];

    [subQuery release]; 
    NSMutableString *columnQuery ==[ [NSMutableString alloc] initWithFormat:@"select AutoID,%@ from tbl_lang where glossary='%@'",lblshortName.text,glossaryName];
    //*****NOTE THIS POINT ----> please make returnExecuteQuery's returned array autorelease if it is not.  
    NSArray *newArray =[[self returnExecuteQuery:columnQuery] mutableCopy] ;
    [langArray addObjectsFromArray:newArray];
    [columnQuery relese];
    NSMutableArray *tempArray = [[NSMutableArray alloc] init];

    for (int i=0; i<[newArray count]; i++) 
        NSMutableString *tempKey = [[NSMutableString  alloc]initWithFormat:@"%@",lblshortName.text]];


        NSString *cellText = [[newArray objectAtIndex:i] valueForKey:tempKey];
        [tempKey release];

        if (cellText != (NSString *)[NSNull null] && ![cellText isEqualToString:@""] ) 
            NSString *decodedString3 = [NSString stringWithUTF8String:[cellText cStringUsingEncoding:[NSString defaultCStringEncoding]]];
            NSMutableString *tempString  = [[NSMutableString alloc] initWithFormat:@"%@ : %@",lblshortName.text, decodedString3]];

            [tempArray addObject:tempString];
            [tempString release];
        
        else 
            [tempArray addObject:@"<empty>"];
        

        NSMutableString *detail =  [[ NSMutableString alloc]initWithString:@"_________________"];

        for (int j=0; j<[lableNameArray count]; j++) 
        

            NSMutableString *key = [[NSMutableString alloc]initWithFormat:@"%@",[lableNameArray objectAtIndex:j]];
            NSString *checkNull=[[subArray1 objectAtIndex:i] valueForKey:key]; //also here if you note you are using subArray1 not subArray?
           [key release];

            if(checkNull != (NSString *)[NSNull null] && checkNull.length > 0)
            
                NSString *decodedString4 = [NSString stringWithUTF8String:[checkNull cStringUsingEncoding:[NSString defaultCStringEncoding]]];
                [detail setString:[NSString stringWithFormat:@"%@\n%@ : %@ ",detail,[lableNameArray objectAtIndex:j],decodedString4]];
               break;//I am not sure why you are checking this condition but assume that you want to get NOT NULL VALUE and  add it to array?
             

           
        [detailTextArray addObject:detail]; 
        [detail release];              

    
    [subArray1 release];
    [newArray release];

更新 2:

if(checkNull != (NSString *)[NSNull null] && checkNull.length > 0)
            
                NSString *decodedString4 = [NSString stringWithUTF8String:[checkNull cStringUsingEncoding:[NSString defaultCStringEncoding]]];
                [detail appendFormat:@"%@\n%@ : %@ ",detail,[lableNameArray objectAtIndex:j],decodedString4]];

            

谢谢,

【讨论】:

感谢代码,但仍然存在一些问题,我想在 for 循环后添加 [detailTextArray addObject:detail]; [detail release];,它显示 detail 中的泄漏。 subArray1 和 newArray 在 Instruments 中仍然显示泄漏。 请看上面的代码并回答代码中的cmets,这样场景可以更清楚。谢谢 在第一个和第二个 cmets 中:数组是自动释放的。第三条评论:我正在检查空值或空白值的条件。如果 checknull 不是空白或 null,则应在详细字符串后面附加 \n。这样我就可以在单元格的详细信息中显示它。 @Meghan,我已经更新了详细字符串附加操作的代码。请确保您正确释放数组【参考方案2】:

不确定是什么导致了内存泄漏,但这可能会有所帮助。这是一种更直接的复制数组的方法,并且可能会导致避免泄漏:

NSArray *langArray =[[NSArray alloc] initWithArray: [self returnExecuteQuery:columnQuery] copyItems: YES];

这基本上是对 returnExecuteQuery 返回的数组进行一级深拷贝。您可以在Collections Programming Topics 中详细了解它。

我不确定 mutableCopy 是如何工作的,这可能与泄漏有关。如果它复制旧数组中的对象,然后将它们添加到新数组中,它们可能会以 2 的保留计数进入数组(1 来自副本,1 来自被添加到数组。)它不会很明显它应该以这种方式工作。但是,如果确实如此,那可能是泄漏的原因。

【讨论】:

@salo.dm:感谢您的回复。在某些情况下它对我有帮助。它仍然在detailsubArray1newArray 中显示泄漏。 如果您使用此代码,则不需要 newArray。您可以删除引用 newArray 的整行。同样,您可以通过将代码中的第 2 行和第 3 行替换为以下代码来取消 subArray1: NSArray *subArray =[[NSArray alloc] initWithArray: [self returnExecuteQuery:subQuery] copyItems: YES]; dm:每次调用此方法时,都会在 langArray 中添加新的数据。所以这对我没有帮助【参考方案3】:

你可以从释放你的 tempArray 开始(在循环之后)。

通常,较高级别的泄漏隐藏在较低级别的洪水中(即容器泄漏会导致其所有内容也泄漏),您的字符串可能就是这种情况。

顺便说一句,使用mutableCopy] autorelease]; 很好。

【讨论】:

以上是关于iPhone SDK 中的 NSMutableArray、NSArray、NSString 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

iphone sdk中的日期操作?

iPhone应用程序中的Facebook SDK登录

UITableViewCell : ContentView 中的 UIImageView 与 iPhone SDK 中的图像重叠。

从次线程访问实例属性(iPhone-SDK)

iphone sdk 中的 NSdateFormatter

MAC iphone SDK中的subversion无法解析用户文件