Created
August 24, 2013 14:18
-
-
Save ipmcc/6328409 to your computer and use it in GitHub Desktop.
A subclass of AVAudioPlayer that retains itself until playback ends or encounters an error, so you don't have to keep a strong reference to it. Supports pass-through to an exogenous delegate that gets set (which is captured in a zeroing weak reference). It's probably inadvisable to use this, but what the heck!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <AVFoundation/AVFoundation.h> | |
@interface OneShotAVAudioPlayer : AVAudioPlayer | |
@end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "OneShotAVAudioPlayer.h" | |
#import <AVFoundation/AVFoundation.h> | |
#import <objc/runtime.h> | |
@interface OneShotAVAudioPlayer () <AVAudioPlayerDelegate> | |
@property(weak) id<AVAudioPlayerDelegate> p_exogenousDelegate; | |
@end | |
@implementation OneShotAVAudioPlayer | |
static void * const OneShotAVAudioPlayerKey = (void*)&OneShotAVAudioPlayerKey; | |
- (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError | |
{ | |
if (self = [super initWithContentsOfURL:url error:outError]) | |
{ | |
// Retain our | |
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN); | |
[super setDelegate: self]; | |
} | |
return self; | |
} | |
- (id)initWithData:(NSData *)data error:(NSError **)outError; | |
{ | |
if (self = [super initWithData:data error:outError]) | |
{ | |
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN); | |
[super setDelegate: self]; | |
} | |
return self; | |
} | |
- (void)setDelegate:(id<AVAudioPlayerDelegate>)delegate | |
{ | |
self.p_exogenousDelegate = delegate; | |
} | |
- (id<AVAudioPlayerDelegate>)delegate | |
{ | |
return self.p_exogenousDelegate; | |
} | |
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag | |
{ | |
@try | |
{ | |
if ([self.p_exogenousDelegate respondsToSelector: _cmd]) | |
[self.p_exogenousDelegate audioPlayerDidFinishPlaying:player successfully:flag]; | |
} | |
@finally | |
{ | |
// Make a strong ref so we stay alive through the scope of this function | |
typeof(self) keepAlive = self; | |
// Give up the self retain | |
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN); | |
// Push in the "real" (outside) delegate, cause our job is done here. | |
[super setDelegate: self.p_exogenousDelegate]; | |
} | |
} | |
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error | |
{ | |
@try | |
{ | |
if ([self.p_exogenousDelegate respondsToSelector: _cmd]) | |
[self.p_exogenousDelegate audioPlayerDecodeErrorDidOccur:player error:error]; | |
} | |
@finally | |
{ | |
// Make a strong ref so we stay alive through the scope of this function | |
typeof(self) keepAlive = self; | |
// Give up the self retain | |
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN); | |
// Push in the "real" (outside) delegate, cause our job is done here. | |
[super setDelegate: self.p_exogenousDelegate]; | |
} | |
} | |
- (BOOL)respondsToSelector:(SEL)aSelector | |
{ | |
BOOL retVal = [super respondsToSelector: aSelector]; | |
if (!retVal) | |
{ | |
struct objc_method_description method = protocol_getMethodDescription(@protocol(AVAudioPlayerDelegate), aSelector, YES, YES); | |
if (method.name) | |
{ | |
retVal = [self.p_exogenousDelegate respondsToSelector: aSelector]; | |
} | |
} | |
return retVal; | |
} | |
- (id)forwardingTargetForSelector:(SEL)aSelector | |
{ | |
id retVal = [super forwardingTargetForSelector:aSelector]; | |
if (!retVal) | |
{ | |
struct objc_method_description method = protocol_getMethodDescription(@protocol(AVAudioPlayerDelegate), aSelector, YES, YES); | |
if (method.name && [self.p_exogenousDelegate respondsToSelector: aSelector]) | |
{ | |
retVal = self.p_exogenousDelegate; | |
} | |
} | |
return retVal; | |
} | |
- (void)setNumberOfLoops:(NSInteger)numberOfLoops | |
{ | |
if (numberOfLoops < 0) | |
{ | |
NSLog(@"Warning! You have set an infinite loop count for an instance of OneShotAVAudioPlayer. This means the instance will effectively be leaked."); | |
} | |
[super setNumberOfLoops: numberOfLoops]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment