【问题标题】:Can someone help me with memory leak?有人可以帮我解决内存泄漏吗?
【发布时间】:2012-09-21 02:33:09
【问题描述】:

编辑:我在项目中使用 ARC

我像这样从 plist 加载注释:

[NSThread detachNewThreadSelector:@selector(loadPList) toTarget:self withObject:nil];

...

- (void) loadPList
{

    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *path = [[documentPaths lastObject] stringByAppendingPathComponent:@"test.plist"];

    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; // memory leak here

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


    dispatch_async(dispatch_get_main_queue(), ^{


        NSMutableArray * annotationsToRemove = [ mapView.annotations mutableCopy ] ;
        [ annotationsToRemove removeObject:mapView.userLocation ] ;
        [ mapView removeAnnotations:annotationsToRemove ] ;



        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"blackKey"])
        {

            NSArray *ann = [dict objectForKey:@"Black"];

            for(int i = 0; i < [ann count]; i++) {

                NSString *coordinates = [[ann objectAtIndex:i] objectForKey:@"Coordinates"];

                double realLatitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:1] doubleValue];
                double realLongitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:0] doubleValue];

                MyAnnotation *myAnnotation = [[MyAnnotation alloc] init];
                CLLocationCoordinate2D theCoordinate;
                theCoordinate.latitude = realLatitude;
                theCoordinate.longitude = realLongitude;

                myAnnotation.coordinate=CLLocationCoordinate2DMake(realLatitude,realLongitude);        
                myAnnotation.title = [[ann objectAtIndex:i] objectForKey:@"Name"];
                myAnnotation.subtitle = [[ann objectAtIndex:i] objectForKey:@"Address"];
                myAnnotation.icon = [[ann objectAtIndex:0] objectForKey:@"Icon"];

                [mapView addAnnotation:myAnnotation];
                [annotations addObject:myAnnotation];

            }

        }   


    });


}

一切正常,但内存泄漏工具显示泄漏。

【问题讨论】:

  • 唯一可能泄漏的方法是如果没有激活的自动释放池。检查您的控制台是否有“刚刚泄漏”的消息。
  • 我认为这是泄漏,因为我将此代码作为新线程运行 [NSThread detachNewThreadSelector:@selector(loadPList) toTarget:self withObject:nil];
  • 如果你在自己的线程中运行它,你需要添加一个自动释放池。
  • (我假设您使用的是 ARC,因为您指定了“自动引用计数”标签。)
  • 好的..我需要把@autoreleasepool { ... }放在哪里?

标签: ios memory-leaks plist automatic-ref-counting nsdictionary


【解决方案1】:

您需要将 @autoreleasepool 放在方法的开头 - 在其外部调用 dictionaryWithContentsOfFile: 时,您将创建一个没有池的自动释放对象,因此它会泄漏。 Per the threading programming guide:

如果您的应用程序使用托管内存模型,则创建一个 自动释放池应该是您在线程条目中做的第一件事 常规。同样,销毁这个自动释放池应该是 你在线程中做的最后一件事。

另外,我能问一下您为什么使用NSThread 来加载plist 而不是dispatch_async() 使用全局队列吗?我不经常看到dispatch_async() 嵌套在线程分离中,所以很好奇。

编辑:

要修复您的内存泄漏,而不干扰您的线程/GCD 混合,请像这样调用您的方法:

[NSThread detachNewThreadSelector:@selector(loadPList) toTarget:self withObject:nil];

并像这样实现它:

- (void) loadPList
{
    @autoreleasepool {
        NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        NSString *path = [[documentPaths lastObject] stringByAppendingPathComponent:@"test.plist"];

        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; // memory leak here

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


        dispatch_async(dispatch_get_main_queue(), ^{

            NSMutableArray * annotationsToRemove = [ mapView.annotations mutableCopy ] ;
            [ annotationsToRemove removeObject:mapView.userLocation ] ;
            [ mapView removeAnnotations:annotationsToRemove ] ;

            if ([[NSUserDefaults standardUserDefaults] boolForKey:@"blackKey"])
            {
                NSArray *ann = [dict objectForKey:@"Black"];

                for(int i = 0; i < [ann count]; i++) 
                {
                    NSString *coordinates = [[ann objectAtIndex:i] objectForKey:@"Coordinates"];

                    double realLatitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:1] doubleValue];
                    double realLongitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:0] doubleValue];

                    MyAnnotation *myAnnotation = [[MyAnnotation alloc] init];
                    CLLocationCoordinate2D theCoordinate;
                    theCoordinate.latitude = realLatitude;
                    theCoordinate.longitude = realLongitude;

                    myAnnotation.coordinate=CLLocationCoordinate2DMake(realLatitude,realLongitude);        
                    myAnnotation.title = [[ann objectAtIndex:i] objectForKey:@"Name"];
                    myAnnotation.subtitle = [[ann objectAtIndex:i] objectForKey:@"Address"];
                    myAnnotation.icon = [[ann objectAtIndex:0] objectForKey:@"Icon"];

                    [mapView addAnnotation:myAnnotation];
                    [annotations addObject:myAnnotation];
                }
            }   
        }
        );
    }
}

【讨论】:

  • 当我这样尝试时: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self loadPList]; });但收到此错误:stackoverflow.com/questions/12654522/…
  • 那么在这种情况下,您正在后台执行整个方法,包括非线程安全部分。在这个问题中,您对loadPlist 的实现与在那个问题中的实现不同-您现在有了正确的想法,将非线程安全部分发送回主队列。无论如何,如果您现在尝试使用 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self loadPList]; }); 的新实现 loadPlist 会发生什么?
  • 我收到信号 SIGABRT 和错误:集合 <__nsarraym:> 在枚举时发生了突变。这里:[mapView addAnnotation:myAnnotation];
  • 移动@autorelease 是否修复了您的内存泄漏?此外,您现在在两个不同的问题中拥有两个不同版本的代码,可帮助您并行解决问题。哪一个是正确的版本?因为现在在这个问题中,您将 @autorelease 放在错误的位置,但正确地将 UI 代码发送回主线程。在另一个问题中,您将自动释放放在正确的位置,但不要将 UI 代码发送回主线程 - 这可能是您崩溃的原因。
  • @PavelKaljunen 我更新了我的答案,明确我在说什么。我会回答你的另一个问题,我认为应该以最正确的方式解决你的两个问题。
【解决方案2】:

如果不出意外,您需要一个自动释放池。引用detachNewThreadSelector 的文档,“aSelector 方法负责为新分离的线程设置一个自动释放池,并在它退出之前释放该池。”

就个人而言,我可能只是通过 GCD 调用 loadPlist 而不是 detachNewThreadSelector,然后您就不必担心自动释放池:

dispatch_async(get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self loadPlist];
});

【讨论】:

  • 应用程序崩溃并出现错误:__NSArrayM 在被枚举时发生了变异
  • 这发生在代码的什么时候?如果它没有向您显示违规行,请尝试添加exception breakpoint,也许这可以帮助您识别它。或者设置一些手动断点,慢慢缩小问题范围。但是看看你上面的代码,我并没有发现这个错误。
【解决方案3】:
NSMutableArray *annotations = [[NSMutableArray alloc]init]; // Never released
NSMutableArray * annotationsToRemove = [ mapView.annotations mutableCopy ] ; // Never released
MyAnnotation *myAnnotation = [[MyAnnotation alloc] init]; // Never released

你的方法应该是这样的:

- (void) loadPList {
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *path = [[documentPaths lastObject] stringByAppendingPathComponent:@"test.plist"];
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; // memory leak here
    NSMutableArray *annotations = [[[NSMutableArray alloc] init] autorelease];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSMutableArray * annotationsToRemove = [[mapView.annotations mutableCopy] autorelease];
        [annotationsToRemove removeObject:mapView.userLocation] ;
        [mapView removeAnnotations:annotationsToRemove] ;
        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"blackKey"])
        {

            NSArray *ann = [dict objectForKey:@"Black"];

            for(int i = 0; i < [ann count]; i++) {

                NSString *coordinates = [[ann objectAtIndex:i] objectForKey:@"Coordinates"];

                double realLatitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:1] doubleValue];
                double realLongitude = [[[coordinates componentsSeparatedByString:@","] objectAtIndex:0] doubleValue];

                MyAnnotation *myAnnotation = [[[MyAnnotation alloc] init] autorelease];
                CLLocationCoordinate2D theCoordinate;
                theCoordinate.latitude = realLatitude;
                theCoordinate.longitude = realLongitude;

                myAnnotation.coordinate=CLLocationCoordinate2DMake(realLatitude,realLongitude);
                myAnnotation.title = [[ann objectAtIndex:i] objectForKey:@"Name"];
                myAnnotation.subtitle = [[ann objectAtIndex:i] objectForKey:@"Address"];
                myAnnotation.icon = [[ann objectAtIndex:0] objectForKey:@"Icon"];

                [mapView addAnnotation:myAnnotation];
                [annotations addObject:myAnnotation];

            }
        }   
    });
}

【讨论】:

  • 那么对不起。我从没用过 ARC,我用的是经典风格 :) init / copy / create - release
  • @Vlad,虽然我尊重你留在 MRC 的决定,但这仍然不是他问题的答案。不幸的是-1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
  • 2021-05-30
  • 2011-12-11
相关资源
最近更新 更多