使用ARC从NSURL和AVAudioPlayer泄漏

时间:2012-09-19 15:34:37

标签: ios memory-leaks instruments nsurl

我是iPhone 4S上的runung Instruments。 我在这个方法中使用AVAudioPlayer:

-(void)playSound{
    NSURL *url = [self.word soundURL];
    NSError *error;
    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    if (!error) {
        [audioPlayer prepareToPlay];
        [audioPlayer play];
    }else{
       NSLog(@"Problem With audioPlayer on general card. error : %@ | url %@",[error description],[url absoluteString]);
}

播放声音文件时出现泄漏:

泄漏的物品:

1

对象:NSURL

负责图书馆:基金会

责任框架:基础 - [NSURL(NSURL)allocWithZone:]

2

对象:_ NSCFString

负责图书馆:基金会

负责框架:基础 - [NSURL(NSURL)initFileURLWithPath:]

仪器没有直接指向我的代码,因此我发现很难找到泄漏原因。

我的问题

什么可能导致泄漏? OR 当我对代码不负责时,如何查找泄漏?

修改 这是Instruments周期视图中的模式: enter image description here 谢谢Shani

5 个答案:

答案 0 :(得分:19)

看起来是Apple代码中的泄漏...我尝试使用两者

  • -[AVAudioPlayer initWithData:error:]
  • -[AVAudioPlayer initWithContentsOfURL:error:]

在第一种情况下,分配的AVAudioPlayer实例保留传入的NSData。在第二个中,传入的NSURL被保留:

我附上了“仪器”窗口的一些屏幕截图,显示了传入NSData对象的保留/释放历史记录。

enter image description here

您可以看到AVAudioPlayer对象然后创建一个C ++对象AVAudioPlayerCpp,它再次保留NSData:

enter image description here

稍后,当AVAudioPlayer对象被释放时,NSData被释放,但是从来没有来自关联AVAudioPlayerCpp的释放调用...(您可以从附加的图像中看出来) )

如果你想避免泄漏NSData / NSURL,你似乎必须使用不同的解决方案来播放媒体..

这是我的测试代码:

-(void)timerFired:(NSTimer*)timer
{
    NSString * path = [[ NSBundle mainBundle ] pathForResource:@"song" ofType:@"mp3" ] ;

    NSError * error = nil ;
    NSData * data = [ NSData dataWithContentsOfFile:path options:NSDataReadingMapped error:&error ] ;
    if ( !data )
    {
        if ( error ) { @throw error ; }
    }

    AVAudioPlayer * audioPlayer = data ? [[AVAudioPlayer alloc] initWithData:data error:&error ] : nil ;
    if ( !audioPlayer )
    {
        if ( error ) { @throw error ; }
    }

    if ( audioPlayer )
    {
        [audioPlayer play];
        [ NSThread sleepForTimeInterval:0.75 ] ;
        [ audioPlayer stop ] ;
    }
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...
    [ NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector( timerFired: ) userInfo:nil repeats:YES ] ;
    // ...
    return YES;
}

答案 1 :(得分:1)

根据之前的答案,我自己的所有研究和苹果开发人员论坛中的讨论都指出苹果自己的iOS版本6.0,6.0.1中的库存在问题,据我所知,6.0.2同样。

iOS 6.1解决了这个问题,我再也看不到漏洞了。

可悲的是,这意味着如果您认为您的应用程序将在仍在运行iOS 6.0,6.0.1或6.0.2版本的手机上运行,​​则必须采取解决方法来解决这些情况的泄漏问题。

可以很好地了解的是,如果其他任何内容已按预期清除,则用于初始化AVAudioPlayer的任何内容的retainCount似乎为1。

我自己的解决方法是将音频封装在自己的类中,我在编译时使用-fno-objc-arc预处理器标志进行手动内存处理。

当谈到释放所有内容的时候,我确保获取我用于初始化的NSURL的保留计数(如果使用NSData初始化,它将工作相同),然后才开始发布它。

如果其他一切都得到了正确处理,那么它现在应该是1或2,所以只需将它释放适当的次数。

此外,请确保在开始发布之前获取保留计数,否则您将尝试访问已修复错误的iOS版本上的已释放对象。

答案 2 :(得分:0)

这是Apple库本身的问题。许多帖子(在stackoverflow上也有)报告了类似的问题。你在这里无能为力。

答案 3 :(得分:0)

据我了解,在ARC项目中工作时,您作为开发人员不再负责保留/发布,因此问题出在Apple的库而不是您的代码中。

答案 4 :(得分:0)

如果这确实是Apple库中的一个错误,那么这是一个不太有趣的解决方法。

对于有问题的类,请将其设置为不启用弧。

这可以通过转到目标的构建阶段来完成 转到编译源下拉列表,找到您的类的.m文件。 输入-fno-objc-arc编译器标志列

不幸的是,您需要调整类并手动管理内存。

有关将类配置为不启用弧的更详细说明,请参阅另一个关于SO的问题的this回答

修改

我想你可以将AVAudioPlayer行为封装到另一个类,并使用该类进行所有播放。在这种情况下,您可以为一个文件设置-fno-objc-arc,而不必处理整个应用程序的内存管理

相关问题