【问题标题】:Help Fix Memory Leak release帮助修复内存泄漏释放
【发布时间】:2011-02-06 17:17:37
【问题描述】:
#import "VTM_AViPodReaderViewController.h"
#import <AudioToolbox/AudioToolbox.h> // for the core audio constants


#define EXPORT_NAME @"exported.caf"

@implementation VTM_AViPodReaderViewController

@synthesize songLabel;
@synthesize artistLabel;
@synthesize sizeLabel;
@synthesize coverArtView;
@synthesize conversionProgress;


#pragma mark init/dealloc
- (void)dealloc {
    [super dealloc];
}

#pragma mark vc lifecycle

-(void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}

#pragma mark event handlers

-(IBAction) convertTapped: (id) sender {
    // set up an AVAssetReader to read from the iPod Library
    NSURL *assetURL = [song valueForProperty:MPMediaItemPropertyAssetURL];
    AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];

    NSError *assetError = nil;
    AVAssetReader *assetReader = [[AVAssetReader assetReaderWithAsset:songAsset
                                                               error:&assetError]
                                  retain];
    if (assetError) {
        NSLog (@"error: %@", assetError);
        return;
    }

    AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderAudioMixOutput 
                                              assetReaderAudioMixOutputWithAudioTracks:songAsset.tracks
                                                                        audioSettings: nil]
                                              retain];
    if (! [assetReader canAddOutput: assetReaderOutput]) {
        NSLog (@"can't add reader output... die!");
        return;
    }
    [assetReader addOutput: assetReaderOutput];

    NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectoryPath = [dirs objectAtIndex:0];
    NSString *exportPath = [[documentsDirectoryPath stringByAppendingPathComponent:EXPORT_NAME] retain];
    if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
        [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
    }
    NSURL *exportURL = [NSURL fileURLWithPath:exportPath];
    AVAssetWriter *assetWriter = [[AVAssetWriter assetWriterWithURL:exportURL
                                                          fileType:AVFileTypeCoreAudioFormat
                                                             error:&assetError]
                                  retain];
    if (assetError) {
        NSLog (@"error: %@", assetError);
        return;
    }
    AudioChannelLayout channelLayout;
    memset(&channelLayout, 0, sizeof(AudioChannelLayout));
    channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
    NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, 
                                    [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
                                    [NSNumber numberWithInt:2], AVNumberOfChannelsKey,
                                    [NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey,
                                    [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
                                    [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
                                    [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                                    [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
                                    nil];
    AVAssetWriterInput *assetWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
                                                                              outputSettings:outputSettings]
                                            retain];
    if ([assetWriter canAddInput:assetWriterInput]) {
        [assetWriter addInput:assetWriterInput];
    } else {
        NSLog (@"can't add asset writer input... die!");
        return;
    }

    assetWriterInput.expectsMediaDataInRealTime = NO;

    [assetWriter startWriting];
    [assetReader startReading];

    AVAssetTrack *soundTrack = [songAsset.tracks objectAtIndex:0];
    CMTime startTime = CMTimeMake (0, soundTrack.naturalTimeScale);
    [assetWriter startSessionAtSourceTime: startTime];

    __block UInt64 convertedByteCount = 0;

    dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);
    [assetWriterInput requestMediaDataWhenReadyOnQueue:mediaInputQueue 
                                            usingBlock: ^ 
     {
         // NSLog (@"top of block");
         while (assetWriterInput.readyForMoreMediaData) {
            CMSampleBufferRef nextBuffer = [assetReaderOutput copyNextSampleBuffer];
            if (nextBuffer) {
                // append buffer
                [assetWriterInput appendSampleBuffer: nextBuffer];
                //              NSLog (@"appended a buffer (%d bytes)", 
                //                     CMSampleBufferGetTotalSampleSize (nextBuffer));
                convertedByteCount += CMSampleBufferGetTotalSampleSize (nextBuffer);
                // oops, no
                // sizeLabel.text = [NSString stringWithFormat: @"%ld bytes converted", convertedByteCount];

                NSNumber *convertedByteCountNumber = [NSNumber numberWithLong:convertedByteCount];
                [self performSelectorOnMainThread:@selector(updateSizeLabel:)
                                       withObject:convertedByteCountNumber
                                    waitUntilDone:NO];
            } else {
                // done!
                [assetWriterInput markAsFinished];
                [assetWriter finishWriting];
                [assetReader cancelReading];
                NSDictionary *outputFileAttributes = [[NSFileManager defaultManager]
                                                      attributesOfItemAtPath:exportPath
                                                      error:nil];
                NSLog (@"done. file size is %ld",
                        [outputFileAttributes fileSize]);
                NSNumber *doneFileSize = [NSNumber numberWithLong:[outputFileAttributes fileSize]];
                [self performSelectorOnMainThread:@selector(updateCompletedSizeLabel:)
                                       withObject:doneFileSize
                                    waitUntilDone:NO];
                // release a lot of stuff
                [assetReader release];
                [assetReaderOutput release];
                [assetWriter release];
                [assetWriterInput release];
                [exportPath release];
                break;
            }
        }

     }];
    NSLog (@"bottom of convertTapped:");
}

-(void) updateSizeLabel: (NSNumber*) convertedByteCountNumber {
    UInt64 convertedByteCount = [convertedByteCountNumber longValue];
    sizeLabel.text = [NSString stringWithFormat: @"%ld bytes converted", convertedByteCount];
}

-(void) updateCompletedSizeLabel: (NSNumber*) convertedByteCountNumber {
    UInt64 convertedByteCount = [convertedByteCountNumber longValue];
    sizeLabel.text = [NSString stringWithFormat: @"done. file size is %ld", convertedByteCount];
}


@end

我在转换时遇到了内存泄漏的重大问题。从我的调试和分析中,它显示:

[assetReader release];
[assetReaderOutput release];
[assetWriter release];
[assetWriterInput release];
[exportPath release];

保留为 1 时不释放。有人可以帮助解决这个问题吗?尝试转换第二首歌曲后,我的应用不断崩溃。

日志报告

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:835:3 Potential leak of an object allocated on line 827 and stored into 'assetReader'

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:849:3 Potential leak of an object allocated on line 841 and stored into 'assetReaderOutput'

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:877:3 Potential leak of an object allocated on line 861 and stored into 'exportPath'

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:877:3 Potential leak of an object allocated on line 870 and stored into 'assetWriter'

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:903:3 Potential leak of an object allocated on line 895 and stored into 'assetWriterInput'

【问题讨论】:

  • 您可以为我们发布堆栈跟踪吗?
  • 到底为什么有人建议将其关闭为“过于具体或以地理为中心”?我欣喜若狂地看到一个带有代码的问题和重要的改变线索!比“我的应用程序泄漏 UIButtons。请帮忙。”好多了。
  • 您是否在模拟器中运行仪器?如果你是,不要!模拟器有时会报告设备上不存在的泄漏。使用真实设备运行泄漏,看看它是否是真正的泄漏
  • @denWombourne 不,用设备运行它。我从我的 iPod 库中选择加载完美的歌曲,转换为 .caf 播放效果很好,但是当我在转换过程中添加一首不同的歌曲时,它会读取字节记录,我相信像内存 1 然后记录内存 2 然后崩溃。我不在我的工作站旁边,所以我无法在这一秒发布堆栈跟踪
  • 也发布崩溃日志。

标签: iphone objective-c xcode core-audio


【解决方案1】:

我已在 if/else 语句之后的 while 循环中使用以下行修复了内存泄漏,以确保真正释放 nextBuffer。

         CMSampleBufferInvalidate(nextBuffer);
         CFRelease(nextBuffer);
         nextBuffer = nil; // NULL?

我在 CMSampleBufferInvalidate 的文档中没有看到任何详细信息,但我发现它在其他地方被引用,它似乎确实有帮助。我也调用 CFRelease 并最终将其设置为 nil。将其设置为 NULL 可能是更合适的方法,因为它可能更适合将其视为 C 级结构,而 NULL 是 C 方式,而 nil 是 Objective-C。我已经通过 Leaks 运行了这段代码,但我没有看到我之前看到的主要泄漏。我仍然看到 128 字节的泄漏。不过,我无法将其追溯到任何代码行。与之前泄露的版本相比,这是一个巨大的改进。

【讨论】:

  • 我在 while 循环中添加了以下行,位于 else 块末尾的 break 语句之前。现在没有内存泄漏。 dispatch_release(mediaInputQueue);
【解决方案2】:

听起来可能像两个独立的问题(如果我理解问题的话); build&analyze 正在识别泄漏。崩溃是一个不同的问题。发布回溯。

一些问题;永远不要直接测试错误以查看是否发生错误。你应该是:

if (!assetReader) {
    ... report assetError and return ...
}

似乎有许多代码路径会泄漏内存;如果canAddOutput: 失败,例如,您不会在返回之前释放assetReader

exportPath不需要保留;如果块需要它,它会保留它。

【讨论】:

  • 对,它在转换第一首歌曲时记录了 5 次内存泄漏,然后在覆盖另一首歌曲后在设备上崩溃。我会尝试你提到的一些事情
  • 我认为主要的罪魁祸首是缓冲区,它将保存大量未释放的数据。我认为assetReader 也是一个问题,但崩溃是由于所有资产数据都没有被释放。一首歌通常在达到极限之前就很适合。
猜你喜欢
  • 2011-03-27
  • 2012-07-15
  • 2011-03-28
  • 2012-06-08
  • 2011-02-18
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
相关资源
最近更新 更多