【问题标题】:Leaking Memory on iPhone :(iPhone 内存泄漏 :(
【发布时间】:2009-06-08 06:40:55
【问题描述】:

我是 C、Obj-C 和 iPhone 的初学者,我正在尝试掌握很多使用的术语。我希望你们中的一个可以帮助解决我这几天一直在努力解决的问题。

我下面的代码是一种调用包含搜索字段和表格的笔尖的方法。该表是通过搜索为下面的“theList”创建的数组来填充的。使用“仪器”,我在线路上遇到了泄漏: NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,@"Name",clientId,@"Id",nil]; ,但我不知道为什么:(

我知道这可能是一个很难回答的问题,但如果有人可以提供帮助!

- (void)editClient:(id)sender {

    if (pickList == nil) {
        pickList = [[PickFromListViewController alloc] initWithNibName:@"PickList" bundle:nil];
    }

    TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSMutableArray *theList = [[NSMutableArray alloc] init];
    int i;
    for (i=0;i < [appDelegate.clients count];i++) {
        Client *thisClient = [appDelegate.clients objectAtIndex:i];
        NSString *clientName = [[NSString alloc] initWithString: thisClient.clientsName];
        NSNumber *clientId = [[NSNumber alloc] init];
        clientId = [NSNumber numberWithInt:thisClient.clientsId];
        NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,@"Name",clientId,@"Id",nil];
        [theList addObject:theItem];
        theItem = nil;
        [clientName release];
        [clientId release];
    }
    [pickList createSearchItems:theList :NSLocalizedString(@"Client",nil)];
    [theList release];

    appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
    [self.navigationController pushViewController:pickList animated:YES];

}

提前致谢!

【问题讨论】:

    标签: iphone objective-c cocoa-touch memory-management


    【解决方案1】:

    这将返回分配的 NSNumber 实例。

    NSNumber *clientId = [[NSNumber alloc] init];
    

    这一行用另一个NSNumber的实例覆盖了上面的clientId,numberWithInt返回autoreleased对象,因为你没有为它分配内存你不应该调用release,它会被自动释放。

    clientId = [NSNumber numberWithInt:thisClient.clientsId];
    

    您在 clientId 上调用 release,因此您遇到了内存问题。 要修复它,请删除上面在这种情况下无用的第一行并将第二行更新为:

    NSNumber * clientId = [NSNumber numberWithInt:thisClient.clientsId];
    

    然后删除:

    [clientId release]
    

    因为clientId会自动释放。

    编辑:重新还是有问题... 我不确定如何在应用委托中操作客户端,否则代码应该可以正常工作,我创建了一个小示例,省略了我看不到的部分(应用委托和客户端):

    // 命令行实用程序 - 基础工具项目:

    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        NSMutableArray * theList = [[NSMutableArray alloc] init];
    
        int i = 0;
        for (i = 0; i < 10; ++i)
        {
            NSString * clientName = [NSString stringWithString:@"client"];  //no need to release
            NSNumber * clientId = [NSNumber numberWithInt:i];
            NSDictionary * theItem = [NSDictionary dictionaryWithObjectsAndKeys:
                                      clientName, @"name",
                                      clientId, @"id",
                                      nil];
    
            [theList addObject:theItem];
        }
    
        for (id item in theList) for (id key in item) NSLog(@"%@ - %@", key, [item objectForKey:key]);
    
        [theList release];
        [pool drain];
        return 0;
    }
    

    【讨论】:

    • 感谢您的宝贵时间!我实际上按照您的建议进行了更改,但是更改了该代码,认为它可以解决问题。它现在按照您的建议返回,但泄漏结果相同。我可以认为问题出在被调用的 nib 上吗?
    • 有一些...... NSCFDictionary、NSCFArray 和 GeneralBlock-16 并指的是那一行。如您所见,我在方法结束时推送了一个新的 ViewController - 在那里我使用 MutableCopy 创建了两个 MutableArrays 来执行对 TableView 的搜索 - 我没有执行深层复制,所以我假设 MutableCopies 仍然与相同的对象对?这两个数组一旦完成,显然会在 ViewController 中释放。再次感谢您的时间和帮助。
    • 嗯根据您的描述很难说,这些步骤中的任何一个都可能有错误。如果在整个操作过程中存在客户端,则可以尝试仅保留对可变数组的引用而不是复制数据。
    • 我会试试的。再次感谢您的时间和帮助。你真是太慷慨了。
    【解决方案2】:

    您正在使用 [[NSNumber alloc] init] 创建 clientID,然后立即使用自动释放的 NSNumber 实例 [NSNumber numberWithInt] 覆盖它,然后稍后在代码中发布它,这是您不应该这样做的。去掉[[NSNumber alloc] init] 行和[clientId release] 行,应该可以稍微修复一下。

    【讨论】:

    • 非常感谢您的回答。我实际上按照您的建议进行了更改,但是更改了该代码,认为它可以解决问题。它现在按照您的建议返回,但泄漏结果相同。我可以认为问题出在被调用的 nib 上吗?
    【解决方案3】:

    除了明显的 NSNumber 泄漏之外,我还需要修复一些其他可能会有所帮助的问题。大多数都相当次要,但根据我使用 Objective-C 的经验,更少的代码 == 更清晰的代码,这对于 Bash 或 Perl 等语言来说并非如此。 ;-)

    - (void) editClient:(id)sender {
      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
      if (pickList == nil) {
        pickList = [[PickFromListViewController alloc] initWithNibName:@"PickList" bundle:nil];
      }
      TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate*)[[UIApplication sharedApplication] delegate];
    
      NSMutableArray *searchItems = [NSMutableArray array];
      NSMutableDictionary *itemDict = [NSMutableDictionary dictionary];
      for (Client *client in appDelegate.clients) {
        [itemDict setObject:[client.clientsName copy]                 forKey:@"Name"];
        [itemDict setObject:[NSNumber numberWithInt:client.clientsId] forKey:@"Id"];
        [searchItems addObject:[[itemDict copy] autorelease]];
      }
      [pickList createSearchItems:searchItems :NSLocalizedString(@"Client",nil)];
      [self.navigationController pushViewController:pickList animated:YES];
      appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
      [pool drain];
    }
    

    有几个让我怀疑的神秘点:

    • for 循环之后的行告诉pickList 对NSMutableArray 做一些事情。该方法应该保留新数组,以及释放旧数组(如果存在)。如果只是覆盖指针,旧数组将被泄露。 (此外,此方法命名不当。匿名参数(前面没有文本的冒号)在 Objective-C 中是合法的,但被认为是非常糟糕的做法。考虑重命名该方法以更好地表达它的作用。)
    • 下一行似乎将选择列表与导航控制器相关联。如果是自定义代码,请确保 -pushViewController:animated: 方法在指定新选择列表时正确释放现有选择列表。
    • 假设分配给appDelegate.returningID 会调用returningID 属性的设置器。确保酒店根据需要保留或复制NSNumber

    即使在 Instruments 中,内存泄漏也很难追踪,而且您经常会发现 Foundation 类(例如 NSDictionary)像筛子一样泄漏,但我一直能够追踪它回到我的代码中的异常。 :-)

    【讨论】:

      猜你喜欢
      • 2010-11-23
      • 2011-12-10
      • 2011-05-24
      • 2023-03-20
      • 2011-09-11
      相关资源
      最近更新 更多