2016-05-31 2 views
4

Je souhaite exécuter plusieurs blocs d'envoi en même temps. Donc, quand un bloc de répartition est en cours en même temps que le 2ème bloc de répartition, je veux arrêter l'exécution du bloc de répartition précédent.Comment arrêter ou annuler l'exécution du bloc d'envoi

J'utilise ce ci-dessous le 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); 
     }); 
    }); 

et moi avons essayé aussi ce ci-dessous le code. mais si je le code ci-dessous, il est possible d'annuler le bloc précédent, mais ma demande va accrocher

//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); 
     }]; 
    }]; 

Merci à l'avance,

+0

Le bloc de répartition dans GCD ne peut pas annuler, mais le bloc dans NSOperationQueue peut annuler. – EricXuan

+0

@EricXuan: Oui, je sais que 'NSOperationQueue' est possible d'annuler Mais quand j'ai utilisé' NSOperationQueue' mon application est bloquée –

+3

@EricXuan - En fait, vous pouvez maintenant annuler dans GCD. (Ce n'est pas aussi élégant que la logique d'annulation dans 'NSOperation'.) Le problème est' dataWithContentsOfURL', car ce n'est pas annulable. Il devrait utiliser 'NSURLSession', qui est annulable. Et il devrait perdre complètement GCD et/ou 'NSOperation', ou s'il veut le garder, il devrait employer la sous-classe asynchrone personnalisable' NSOperation' annulable, et avoir la méthode 'cancel' annuler la tâche (alors que c'est une solution plus riche que juste 'NSURLSession' en soi, c'est aussi un peu plus de travail). – Rob

Répondre

2

Aucune file d'attente d'expédition ou des files d'attente d'opération sont nécessaires ici.

Vous devez juste être en mesure de démarrer une session de téléchargement asynchrone avec NSURLSession et lorsque le téléchargement est réussi, démarrez le AVAudioPlayer asynchrone. Et parce que ce sont des tâches asynchrones, vous pouvez soit cancel ou stop respectivement.

Voici un exemple trivial:

@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 

Vous pouvez donc voir que downloadAndPlay initie le téléchargement asynchrone et, quand cela est fait, commence la lecture asynchrone de la piste. La méthode cancel annule le téléchargement s'il est en cours et arrête la lecture s'il est en cours.

Et puis vous pouvez l'utiliser comme ceci:

@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 

Maintenant, clairement, cette une implémentation triviale (vous ne voulez pas vraiment faire NSAssert si vous avez des erreurs, mais plutôt gérer gracieusement, vous voulez gérer une série d'objets, vous pouvez découpler le téléchargement de la lecture pour que vous puissiez commencer à télécharger la chanson 2 pendant que la chanson 1 joue, etc.), mais cela illustre le concept plus large d'annuler un téléchargement ou le jeu de une chanson, les deux étant déjà des tâches asynchrones, donc aucune file d'attente d'envoi ou de file d'attente d'opération n'est nécessaire. Vous pouvez le faire si vous voulez avoir envie, mais c'est un sujet plus avancé. Par ailleurs, NSURLSession est assez strict sur l'interdiction des demandes non-https en raison du risque de sécurité qu'ils posent, mais vous pouvez éditer votre info.plist (faites un clic droit dessus puis dites "Ouvrir comme" - "Code source") et puis entrez quelque chose comme:

<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> 
+0

Merci, Laissez-moi essayer votre solution –