帮助修复内存泄漏版本
#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'
#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
I'm having major problems with memory leak while converting. From my Debug and Analysis, it shows:
[assetReader release];
[assetReaderOutput release];
[assetWriter release];
[assetWriterInput release];
[exportPath release];
with a retain of 1 not releasing. Can someone help fix this problem? My app keeps crashing after a try to convert a second song.
Log report
/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'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在 if/else 语句之后的 while 循环中使用以下几行修复了内存泄漏,以确保 nextBuffer 真正被释放。
我在 CMSampleBufferInvalidate 的文档中没有看到任何详细信息,但我发现它在其他地方引用,而且它似乎确实有帮助。我还调用了CFRelease,最后将其设置为nil。将其设置为 NULL 可能是更合适的方法,因为可能更愿意将其视为 C 级别结构,而 NULL 是 C 方式,而 nil 是 Objective-C 方式。我已经通过泄漏运行了这段代码,但没有看到我之前看到的主要泄漏。我仍然看到 128 字节的泄漏。但我无法追溯到任何代码行。与之前的泄漏版本相比,这是一个巨大的改进。
I have fixed the memory leak with the following lines in the while loop after the if/else statement to ensure that nextBuffer is really released.
I do not see any details in the docs on CMSampleBufferInvalidate but I found it referenced elsewhere and it does seem to help. I also call CFRelease and finally set it to nil. Setting it to NULL may be the more proper way to go since it may be preferred to treat this as a C level struct and NULL is the C way and nil is Objective-C. I've run this code though Leaks and I am not seeing the major leaks that I saw before. I do still see a leak of 128 bytes. I cannot trace it back to any line of code though. This is a huge improvement over the previously leaky version.
听起来可能像是两个独立的问题(如果我理解这个问题);构建和分析正在识别泄漏。崩溃是另一个问题。发布回溯。
一些问题;永远不要直接测试错误来查看错误是否发生。您应该:
似乎有许多代码路径会泄漏内存;例如,如果
canAddOutput:
失败,则您在返回之前不会释放assetReader
。exportPath
不需要保留;如果该块需要它,它将保留它。Sounds, possibly, like two separate issues (if I understand the question); build&analyze is identifying a leak. A crash is a different issue. Post the backtrace.
Some issues; never test an error directly to see if an error happened. You should be:
There appear to be a number of code paths where you'll leak memory; if
canAddOutput:
fails, for example, you aren't releasingassetReader
prior to return.exportPath
does not need to be retained; if the block needs it, it'll retain it.