简体   繁体   中英

AVAudioPlayer not playing without IVAR

Before I learned about categories, I had a declared a method for my entity in the generated NSManagedObject Subclass:

// UserRecording.h

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

@interface UserRecording : NSManagedObject {
    AVAudioPlayer *audioPlayer;
}

@property (nonatomic, retain) NSDate * dateCreated;
@property (nonatomic, retain) NSData * audioData;
@property (nonatomic, retain) NSString * name;

-(void) URplay;

@end

and here's the implementation:

// UserRecording.m


#import "UserRecording.h"

@implementation UserRecording

@dynamic dateCreated;
@dynamic audioData;
@dynamic name;

-(void) URplay {
    NSError *error;
    audioPlayer = [[AVAudioPlayer alloc] initWithData:self.audioData error:&error];
   [audioPlayer play];
}

@end

When I learned about categories (via the Stanford iTunes U videos), I moved the code to a category. But the sound stopped playing. The only difference was that there was no declared instance variable (IVAR). Indeed I tested it in my old code. The above code plays audio, but this code doesnt (in the Simulator):

// UserRecording.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface UserRecording : NSManagedObject

@property (nonatomic, retain) NSDate * dateCreated;
@property (nonatomic, retain) NSData * audioData;
@property (nonatomic, retain) NSString * name;

-(void) URplay;

@end

and the implementation:

// UserRecording.m

#import "UserRecording.h"
#import <AVFoundation/AVFoundation.h>

@implementation UserRecording

@dynamic dateCreated;
@dynamic audioData;
@dynamic name;

-(void) URplay {
    NSError *error;
    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:self.audioData error:&error];
   [audioPlayer play];
}

@end

Maybe it have something to do with ARC? But regardless, what can I do? You can't declare an instance variable in a category so that won't do.

It has to do with ARC in the sense that your AVAudioPlayer is getting deallocated right after you instantiate it, because with ARC local variables are deallocated when you leave the scope, try declaring a property for it, you don't need an ivar per se:

In header file or interface declaration in the implementation:

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

In implementation:

@synthesize audioPlayer = _audioPlayer;

This last part implicitly creates an ivar _audioPlayer for your property. You can access it directly, but I'd suggest still going throw the setter self.audioPlayer = .

I'm paraphrasing what I learned on the Apple Developer Forums:

Due to ARC, local variables will be deallocated when they leave scope. So the AVAudioPlayer declared in the method will vanish before it gets a chance to play. While adding an IVAR is possible in some contexts, here it isn't. The reason is that you cannot add IVARs to categories. To elaborate, the above NSManagedObject subclass is generated by XCode - if I added an IVAR to the entity, it would get overwritten by XCode when I changed the entity & regenerated.

Therefore, the solution is to rethink where my persistent AVAudioPlayer should go. I'll probably add a persistent AVAudioPlayer* as an argument to the category's method and pass it along when the method gets called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM