2016-05-31 21 views
4

Ich möchte mehrere Dispatch-Blöcke gleichzeitig ausführen. Wenn also zum gleichen Zeitpunkt ein Dispatch-Block läuft, während ich den zweiten Dispatch-Block betreibe, möchte ich die Ausführung des vorherigen Dispatch-Blocks stoppen.So stoppen oder brechen Sie die Ausführung des Dispatchblocks ab

Ich verwende diesen Code unten:

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

Und ich habe auch versucht, diesen Code unten. aber wenn ich diesen Code unten verwendet wird, ist es möglich vorherigen Block zu löschen, aber meine Anwendung geht

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

Vielen Dank im Voraus hängen,

+0

Der Dispatchblock in GCD kann nicht abgebrochen werden, aber der Block in NSOperationQueue kann abgebrochen werden. – EricXuan

+0

@EricXuan: Ja, ich weiß 'NSOperationQueue' kann abgebrochen werden Aber wenn ich' NSOperationQueue' verwendet habe, ist meine Anwendung fest –

+3

@EricXuan - Eigentlich können Sie jetzt in GCD abbrechen. (Es ist einfach nicht so elegant wie die Abbruchlogik in 'NSOperation'.) Das Problem ist' dataWithContentsOfURL', da das nicht abbrechen kann. Er sollte "NSURLSession" verwenden, was kündbar ist. Und er sollte GCD und/oder "NSOperation" vollständig verlieren, oder wenn er es behalten möchte, sollte er die abbrechbare benutzerdefinierte asynchrone "NSOperation" Unterklasse verwenden und die 'cancel' Methode die Aufgabe abbrechen (während dies eine reichhaltigere Lösung ist als nur "NSURLSession" an sich, es ist auch ein bisschen mehr Arbeit). – Rob

Antwort

2

Keine Versand Warteschlangen oder Betrieb Warteschlangen werden hier benötigt.

Sie müssen nur in der Lage sein, eine asynchrone Download-Sitzung mit NSURLSession zu starten, und wenn der Download erfolgreich ist, starten Sie die asynchrone AVAudioPlayer. Und weil diese asynchronen Aufgaben sind, können Sie entweder cancel oder stop diese jeweils.

hier ein triviales Beispiel ist:

@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 können Sie sehen, dass downloadAndPlay asynchron initiiert herunterladen und, wenn das erledigt ist, beginnt die asynchrone Wiedergabe der Spur. Die cancel-Methode bricht den Download ab, wenn sie ausgeführt wird, und stoppt die Wiedergabe, wenn sie ausgeführt wird.

Und dann kann man es wie so verwenden:

@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 

nun klar, das ist eine triviale Implementierung (Sie wollen nicht wirklich NSAssert tun, wenn Sie Fehler haben, sondern behandeln sie anmutig, Sie möchten eine Reihe von Song Objekte behandeln, möchten Sie vielleicht Download vom Spielen entkoppeln, so dass Sie Song 2 herunterladen können, während Song 1 spielt, etc.), aber es veranschaulicht das breitere Konzept des Abbrechens entweder einen Download oder das Abspielen von ein Lied, die beide bereits asynchrone Aufgaben sind, so dass keine Versandwarteschlangen oder Vorgangswarteschlangen benötigt werden. Sie können das tun, wenn Sie Lust haben, aber das ist ein fortgeschrittenes Thema.

By the way, ist NSURLSession ziemlich streng über das Verbot nicht-HTTPS-Anfragen wegen des Sicherheitsrisikos stellen sie, aber Sie können Ihre info.plist (rechts klicken Sie darauf und dann sagen: „Öffnen als“ - „Quellcode“) bearbeiten und geben Sie dann Folgendes ein:

<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

Danke, lass mich deine Lösung versuchen –