gpt4 book ai didi

ios - 播放声音后 childViewController 不释放

转载 作者:行者123 更新时间:2023-11-29 03:21:15 24 4
gpt4 key购买 nike

我有一个 pageViewController (UIPageViewController),它有一个 subview Controller (名为 soundPlayer)。声音播放器有静态 AVAudioPLayer *player在页面之间导航时,我们必须有播放器(单例模式)。所以如果我在页面之间导航而不播放声音,最后从这些 View Controller (导航 Controller )返回,soundPlayer 将被释放。但是当我播放声音并在页面之间导航时,每翻一页,新的 soundPlayer 都不会释放,并且会创建一个新的 soundPlayer !

在每次翻页播放声音之前,只有我们在soundPlayerViewController上,但是当我们每次翻页播放声音时,soundPlayerViewController并没有释放,活体对象增加了。

播放声音之前 enter image description here

播放声音后

enter image description here

忽略我的真实应用程序中的那些泄漏,我没有它们

    #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

@class AppScrollView;


@interface SoundPlayerViewController : UIViewController <AVAudioPlayerDelegate>{


BOOL paused;
BOOL inBackground;
NSTimer *updateTimer;
UIImage *playBtnBG;
UIImage *pauseBtnBG;
UIImage *bookmarkedBtnBG;
}


@property (weak, nonatomic) IBOutlet UILabel *duration;
@property (weak, nonatomic) IBOutlet UILabel *currentTime;
@property (weak, nonatomic) IBOutlet UISlider *progressBar;
@property (nonatomic, strong) NSTimer *updateTimer;
@property (nonatomic, assign) BOOL inBackground;
@property (weak, nonatomic) IBOutlet UIButton *playButton;

- (IBAction)playButtonPressed:(UIButton *)sender;

- (IBAction)progressSliderMoved:(UISlider *)sender;

- (void)updateViewForPlayerState:(AVAudioPlayer *)player;
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)player;
- (void)updateViewForPlayerInfo:(AVAudioPlayer *)player;
- (void)updateCurrentTimeForPlayer:(AVAudioPlayer *)player;
- (void)registerForBackgroundNotifications;
- (void)updateBookmarkButton;

- (void)stopSoundPlayer;

@end

SoundPlayerViewController.m

#import "SoundPlayerViewController.h"

@interface SoundPlayerViewController ()

- (void)customizeAppearance;

@end

@implementation SoundPlayerViewController

static AVAudioPlayer *soundPlayer;

@synthesize numberFormatter;
@synthesize duration;
@synthesize currentTime;
@synthesize progressBar;
@synthesize updateTimer;
@synthesize inBackground;
@synthesize playButton;

#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self customizeAppearance];
paused = true;

[self registerForBackgroundNotifications];

NSError *error = nil;

NSString *fileName = @"sample";
NSString *fileType = @"m4a";
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
NSURL *soundURL = [NSURL fileURLWithPath:soundFilePath];
//soundPalyer is Static so there is one for all of instance objects of this class.
if (!soundPlayer) {

soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];

if (soundPlayer) {
[self updateViewForPlayerState:soundPlayer];
[self updateViewForPlayerInfo:soundPlayer];
soundPlayer.numberOfLoops = 0;
soundPlayer.delegate = self;
updateTimer = nil;
}
}

}

-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//if you pause soundPlayer and curl page you will notice soundPlayer won't update so
//I update sound player
[self updateViewForPlayerState:soundPlayer];
}

- (void)viewWillDisappear:(BOOL)animated
{
[self updateViewForPlayerState:soundPlayer];
[super viewWillDisappear:animated];

}

#pragma mark - my custom functions
- (void)customizeAppearance
{
//change slider appearacne
UIImage *minImage = [[UIImage imageNamed:@"sliderMinTrack"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
UIImage *maxImage = [[UIImage imageNamed:@"sliderMaxTrack"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 9)];
UIImage *thumbImage = [UIImage imageNamed:@"sliderThumb"];

[progressBar setMaximumTrackImage:maxImage
forState:UIControlStateNormal];
[progressBar setMinimumTrackImage:minImage
forState:UIControlStateNormal];
[[UISlider appearance] setThumbImage:thumbImage
forState:UIControlStateNormal];
[[UISlider appearance] setThumbImage:thumbImage
forState:UIControlStateHighlighted];


[playButton setImage:playBtnBG forState:UIControlStateNormal];


}

- (void)pausePlaybackForPlayer:(AVAudioPlayer *)player
{
[player pause];
[self updateViewForPlayerState:player];


}

- (void)startPlaybackForPlayer:(AVAudioPlayer *)player
{
//I must add paused = false here, else if I stop playing and bring front the app from background, sound player continue playing.
paused = false;
player.enableRate = YES;
[player prepareToPlay];
[player play];
[self updateViewForPlayerState:player];
}
- (IBAction)playButtonPressed:(UIButton *)sender
{

if (soundPlayer.playing == YES) {
[self pausePlaybackForPlayer:soundPlayer];
//I use this flag to control when the audio should be played
paused = true;
} else {
[self startPlaybackForPlayer:soundPlayer];
}

}

- (IBAction)progressSliderMoved:(UISlider *)sender
{
soundPlayer.currentTime = sender.value;
progressBar.maximumValue = soundPlayer.duration;

[self updateViewForPlayerState:soundPlayer];
}

- (void)stopSoundPlayer
{
if (self.mailPickerIsPresented) {
if ([soundPlayer isPlaying]) {
self.mailPickerhasPausedPlayback = true;
[self pausePlaybackForPlayer:soundPlayer];
}
}
else
{
[soundPlayer stop];
soundPlayer = nil;
}
}

- (void)updateViewForPlayerState:(AVAudioPlayer *)player
{
if (updateTimer)
[updateTimer invalidate];

if (player.playing) {

if(pauseBtnBG)
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}

updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(updateCurrentTime) userInfo:player repeats:YES];
} else {

[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
//I called this function below to set currentTimeLabel=0 after finishig playing
//[self updateCurrentTimeForPlayer:soundPlayer];

updateTimer = nil;
}
}

- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)player
{
[self updateCurrentTimeForPlayer:player];

if (player.playing)
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
else
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
}

-(void)updateViewForPlayerInfo:(AVAudioPlayer*)player
{
duration.text = [NSString stringWithFormat:@"%d:%02d",(int)player.duration / 60, (int)player.duration % 60, nil];
progressBar.maximumValue = player.duration;
}

- (void)updateCurrentTime
{
[self updateCurrentTimeForPlayer:soundPlayer];
}

- (void)updateCurrentTimeForPlayer:(AVAudioPlayer *)player
{
currentTime.text = [NSString stringWithFormat:@"%d:%02d", (int)soundPlayer.currentTime / 60, (int)soundPlayer.currentTime % 60, nil];
progressBar.value = player.currentTime;}

#pragma mark background notifications
- (void)registerForBackgroundNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setInBackgroundFlag)
name:UIApplicationWillResignActiveNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearInBackgroundFlag)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}

- (void)setInBackgroundFlag
{
inBackground = true;
[self pausePlaybackForPlayer:soundPlayer];

}

- (void)clearInBackgroundFlag
{

inBackground = false;
//we are checking if palyer was playing, if so, we continue its playing.
if (paused == false) {
[self startPlaybackForPlayer:soundPlayer];
}
}

#pragma mark AVAudioPlayer delegate methods

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{if (flag == NO)
NSLog(@"Playback finished unsuccessfully");

[player setCurrentTime:0.0];
if (inBackground)
{
[self updateViewForPlayerStateInBackground:player];
}
else
{
[self updateViewForPlayerState:player];
}
}


// we will only get these notifications if playback was interrupted
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)p
{
NSLog(@"Interruption begin. Updating UI for new state");
// the object has already been paused, we just need to update UI
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}

- (void)audioPlayerEndInterruption:(AVAudioPlayer *)p
{
NSLog(@"Interruption ended. Resuming playback");
[self startPlaybackForPlayer:p];
}


@end

我已经上传了示例应用程序

https://www.dropbox.com/s/9fxqsly62euhr8p/pageBasedApp.zip

最佳答案

我建议您创建单例来管理您的 AVAudioPlayer,如下所示

界面

// AudioManager.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface AudioManager : NSObject

@property (nonatomic, strong) AVAudioPlayer *soundPlayer;

+ (id)sharedManager;

@end

// AudioManager.m
#import "AudioManager.h"
#define kSoundFileName @"sample.m4a"

实现

@implementation AudioManager

+ (id)sharedManager
{
static AudioManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}

- (AVAudioPlayer *)soundPlayer
{
NSError *error;
if (_soundPlayer == nil)
{
_soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[self soundURL] error:&error];
}
return _soundPlayer;
}

- (NSURL *)soundURL
{
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"bgMusic" ofType:@"mp3"];
return [NSURL fileURLWithPath:soundFilePath];
}

@end

在其他 View Controller 中,您可以在单例对象上引用 AVAudioPlayer 属性,其中只会创建单个 AudioManager 对象,如下所示

 #import "AudioManager.h"

[[[AudioManager sharedManager] soundPlayer] play];

关于ios - 播放声音后 childViewController 不释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21062611/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com