I want to execute multiple dispatch block at same time. So when any dispatch block is in progress at same time when I run 2nd dispatch block, I want to stop execution of previous dispatch block.
I'm using this below code :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:item.URL];
dispatch_async(dispatch_get_main_queue(), ^(void){
NSError *error = nil;
self.player =[[AVAudioPlayer alloc] initWithData:data fileTypeHint:AVFileTypeMPEGLayer3 error:&error];
NSLog(@"%@",error);
});
});
And I also tried this below code. but if I used this below code it is possible to cancel previous block but my application goes hang
//Use NSOperationQueue
myQueue = [NSOperationQueue mainQueue];
[myQueue addOperationWithBlock:^{
// Background work
NSData *data = [NSData dataWithContentsOfURL:item.URL];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
NSError *error = nil;
self.player =[[AVAudioPlayer alloc] initWithData:data fileTypeHint:AVFileTypeMPEGLayer3 error:&error];
NSLog(@"%@",error);
}];
}];
Thanks in advance,
No dispatch queues or operation queues are needed here.
You just need to be able to start an asynchronous download session with NSURLSession
and when the download is successful, start the asynchronous AVAudioPlayer
. And because those are asynchronous tasks, you can either cancel
or stop
them respectively.
Here is a trivial example:
@class Song;
@protocol SongDelegate <NSObject>
- (void)song:(Song *)song didFinishPlayingSuccessfully:(BOOL)flag;
- (void)song:(Song *)song didFinishDownloadWithError:(NSError *)error;
@end
@interface Song: NSObject <AVAudioPlayerDelegate>
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, weak) NSURLSessionTask *downloadTask;
@property (nonatomic, strong) NSURL *localURL;
@property (nonatomic, strong) AVAudioPlayer *player;
@property (nonatomic, weak) id<SongDelegate> delegate;
@end
@implementation Song
+ (instancetype)songWithURL:(NSURL *)url delegate:(id<SongDelegate>)delegate {
Song *song = [[Song alloc] init];
song.url = url;
song.delegate = delegate;
return song;
}
- (void)downloadAndPlay {
self.downloadTask = [[NSURLSession sharedSession] downloadTaskWithURL:self.url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate song:self didFinishDownloadWithError:error];
});
NSURL *documentsURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:&error];
NSAssert(documentsURL, @"URLForDirectory failed: %@", error);
NSURL *fileURL = [documentsURL URLByAppendingPathComponent:self.url.lastPathComponent];
NSError *moveError;
BOOL success = [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&moveError];
NSAssert(success, moveError.localizedDescription);
// note, the only reason we dispatch the following is that this completion handler runs on background queue and we want to update properties and start the player from the main queue
dispatch_async(dispatch_get_main_queue(), ^{
self.localURL = fileURL;
NSError *playError;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&playError];
self.player.delegate = self;
[self.player play];
NSAssert(playError == nil, playError.localizedDescription);
});
}];
[self.downloadTask resume];
}
- (void)cancel {
[self.downloadTask cancel]; // if download still in progress, stop it
[self.player stop]; // if playing, stop it
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
self.player = nil;
[self.delegate song:self didFinishPlayingSuccessfully:flag];
}
@end
So you can see that downloadAndPlay
initiates asynchronous download and, when that's done, starts the asynchronous playing of the track. The cancel
method cancels the download if in progress and stops the playing if in progress.
And then you can use it like so:
@interface ViewController () <SongDelegate>
@property (nonatomic, strong) Song *song;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.song = [Song songWithURL:[NSURL URLWithString:@"http://dl.last.fm/static/1464677535/131211148/70b3b5a9d048c7939d5bb9ec87a2c5d58d6ee528828f5c6a5b7b1eddd69f4553/Death+Grips+-+Get+Got.mp3"] delegate:self];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)didTapPlayButton:(id)sender {
[self.song downloadAndPlay];
}
- (IBAction)didTapStopButton:(id)sender {
[self.song cancel];
}
- (void)song:(Song *)song didFinishPlayingSuccessfully:(BOOL)flag {
NSLog(@"did finish playing %@", flag ? @"successfully" : @"unsuccessfully");
}
- (void)song:(Song *)song didFinishDownloadWithError:(NSError *)error {
NSLog(@"did finish download with error %@", error.localizedDescription);
}
@end
Now, clearly, this a trivial implementation (you don't really want to do NSAssert
if you have any errors, but rather handle it gracefully, you want to handle a series of Song
objects, you may want to decouple downloading from playing so that you can start downloading song 2 while song 1 plays, etc.), but it illustrates the broader concept of canceling either a download or the playing of a song, both of which are already asynchronous tasks, so no dispatch queues or operation queues are needed. You can do that if you want to get fancy, but that's a more advanced topic.
By the way, NSURLSession
is pretty strict about prohibiting non-https requests because of the security risk they pose, but you can edit your info.plist
(right click on it and then say "Open As" - "Source code") and then enter something like:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>dl.last.fm</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--Include to specify minimum TLS version-->
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>