ios – 如何停止或取消调度块的执行

我想同时执行多个调度块.因此,当我运行第二个调度块时,当任何调度块同时进行时,我想停止执行先前的调度块.

我在下面使用以下代码:

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

我也尝试了以下代码.但如果我使用下面的代码,可以取消上一个块但我的应用程序挂起

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

提前致谢,

最佳答案 此处不需要调度队列或操作队列.

您只需要能够使用NSURLSession启动异步下载会话,并在下载成功时启动异步AVAudioPlayer.因为这些是异步任务,您可以分别取消或停止它们.

这是一个简单的例子:

@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

因此,您可以看到downloadAndPlay启动异步下载,并在完成后启动异步播放.取消方法取消正在进行的下载,并在正在进行时停止播放.

然后你可以像这样使用它:

@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

现在,显然,这是一个简单的实现(如果你有任何错误,你真的不想做NSAssert,而是优雅地处理它,你想要处理一系列Song对象,你可能想要将下载与播放分离,以便你可以在歌曲1播放时开始下载歌曲2等,但它说明了取消下载或播放歌曲的更广泛的概念,这两者都是异步任务,因此不需要调度队列或操作队列.如果你想获得幻想,你可以这样做,但那是一个更高级的话题.

顺便说一句,NSURLSession非常严格禁止非https请求,因为它们带来了安全风险,但你可以编辑你的info.plist(右键单击它然后说“打开为” – “源代码”)然后输入类似的东西:

<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>
点赞