2014-01-11 276 views
0

我有一個pageViewController(UIPageViewController)它有一個子視圖控制器(名爲soundPlayer)。 聲音播放器有static AVAudioPLayer *player 在頁面之間導航時,我們必須在播放器(單例模式)上進行導航。 因此,如果我在沒有播放聲音的頁面之間導航,最後從這些視圖控制器(導航控制器)返回soundPlayer將被釋放。 但是當我播放聲音並在頁面之間導航時,每翻頁一個新的soundPlayer都不會釋放,並且會創建一個新的!childViewController不播放後播放聲音

之前播放聲音與每頁翻轉,只有我們有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

回答

0

簡短的回答:Don'e使用靜態變量。你不明白他們是如何工作的,並且錯誤地使用它們。 您應該刪除聲音播放器上的「靜態」限定符。

+0

我知道這是因爲這一點。但我只需要一個AVAudioPlayer就可以通過頁面訪問。所以我把它變成了靜態的。爲了達到這個目的,是否知道其他方式? –

1

我會建議你創建辛格爾頓如下

接口

// 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 

,你可以參考AVAudioPlayer財產上單來管理您的AVAudioPlayer對象,其中只創建一個AudioManager對象,如下所示:

#import "AudioManager.h" 

[[[AudioManager sharedManager] soundPlayer] play];