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

Ios 如何停止或取消调度块的执行,ios,objective-c,nsoperationqueue,dispatch-async,Ios,Objective C,Nsoperationqueue,Dispatch Async,我想同时执行多个分派块。所以,当我运行第二个分派块的同时,任何分派块都在进行中时,我想停止前一个分派块的执行 我正在使用以下代码: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *data = [NSData dataWithContentsOfURL:item.URL]; dispatch_async(dispatch_get_mai

我想同时执行多个分派块。所以,当我运行第二个分派块的同时,任何分派块都在进行中时,我想停止前一个分派块的执行

我正在使用以下代码:

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
现在,很明显,这是一个微不足道的实现(如果您有任何错误,您并不真正想执行
NSAssert
,而是要优雅地处理它,您想处理一系列
Song
对象,您可能想将下载与播放分离,以便在播放Song 1时开始下载Song 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>
NSAppTransportSecurity
NSExceptionDomains
dl.last.fm
n包括多个域
NSTemporary ExceptionalLowsInSecureHttpLoads
NSTemporaryExceptionMinimumTLSVersion
TLSv1.1

GCD中的调度块不能取消,但NSOperationQueue中的调度块可以取消。@EricXuan:是的,我知道
NSOperationQueue
可以取消,但当我使用
NSOperationQueue时,我的应用程序被禁用stuck@EricXuan-实际上,你现在可以在GCD中取消。(它不像
NSOperation
中的取消逻辑那样优雅)问题在于
dataWithContentsOfURL
,因为这是不可取消的。他应该使用可取消的
NSURLSession
。他应该完全丢失GCD和/或
NSOperation
,或者如果他想保留它,他应该使用可取消的自定义异步
NSOperation
子类,并使用
cancel
方法取消任务(虽然这是一个比
nsursession
本身更丰富的解决方案,但它也需要做更多的工作)@Moni_BQ-您可以在
NSOperation
上调用cancel,但这只是解决方案的一半。您还必须编写一个实际响应取消事件的异步
NSOperation
子类,否则对
cancel
的调用将不会取消正在进行的任何操作(只会有效地停止那些尚未开始的操作,这是最小值)。您确实需要两件事:您希望能够取消正在进行的下载(这就是为什么您希望使用
NSURLSession
,假设
item.URL
是远程URL而不是本地文件URL)。您还希望能够停止播放歌曲。所以只要在
AVAudioPlayer
上调用
stop
。这两项任务都不需要GCD或
NSOperation
。如果
item.URL
是远程web URL,您只能使用
NSURLSession
代替
dataWithContentsOfURL
。谢谢,让我试试您的解决方案
<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>