20

我在 iPhone 4S 上运行 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:]

Instruments 没有直接指向我的代码,所以我很难找到泄漏的原因。

我的问题

什么可能导致泄漏? 或者当我不对代码负责时,如何定位泄漏?

编辑 这是仪器周期视图的模式: 在此处输入图像描述 谢谢 Shani

4

5 回答 5

19

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

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

在第一种情况下,分配的AVAudioPlayer实例保留传入的NSData. 在第二种情况下,传入的NSURL内容被保留:

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

在此处输入图像描述

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

在此处输入图像描述

后来,当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;
}
于 2012-09-30T07:58:39.360 回答
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 版本上访问已释放的对象。

于 2013-02-07T22:01:14.013 回答
0

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

于 2012-09-25T19:20:39.453 回答
0

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

于 2012-09-25T18:11:55.850 回答
0

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

对于有问题的类,使其不启用弧。

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

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

关于将类配置为不启用弧的更详细描述是关于 SO 上另一个问题答案

编辑

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

于 2012-09-27T23:59:56.447 回答