Iphone 为什么主队列上的GCD dispatch_async会导致后台队列出现死锁?

Iphone 为什么主队列上的GCD dispatch_async会导致后台队列出现死锁?,iphone,ios,objective-c,concurrency,grand-central-dispatch,Iphone,Ios,Objective C,Concurrency,Grand Central Dispatch,我正在创建一个串行后台队列,如下所示: @property (nonatomic, strong) dispatch_queue_t assetCreationQueue; // in init... _assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL); [group enumerateAssetsUsingBlock:^(ALAsset

我正在创建一个串行后台队列,如下所示:

@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;

// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
    if (asset){
        dispatch_async(weakSelf.assetCreationQueue, ^{
            ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
            NSURL *url = [assetRepresentation url];
            if (url) {
                AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

                // This NSLog fires! avAsset exists.
                NSLog(@"AVURLAsset %@", avAsset);

                dispatch_async(dispatch_get_main_queue(), ^{
                    // This NSLog NEVER fires.
                    // Also tried dispatch_sync.
                    NSLog(@"add to assets array on main queue");
                    [weakSelf.assets insertObject:avAsset atIndex:0];
                });
            }
        });
    }
}];
然后,我在背景中列举了一组对象,如下所示:

@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;

// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
    if (asset){
        dispatch_async(weakSelf.assetCreationQueue, ^{
            ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
            NSURL *url = [assetRepresentation url];
            if (url) {
                AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

                // This NSLog fires! avAsset exists.
                NSLog(@"AVURLAsset %@", avAsset);

                dispatch_async(dispatch_get_main_queue(), ^{
                    // This NSLog NEVER fires.
                    // Also tried dispatch_sync.
                    NSLog(@"add to assets array on main queue");
                    [weakSelf.assets insertObject:avAsset atIndex:0];
                });
            }
        });
    }
}];
资产数组属性定义为:

@property (nonatomic, strong) NSMutableArray *assets;
当我尝试
dispatch_sync(dispatch_get_main_queue(),^{
时,我在控制台中只得到一个
NSLog(@“avurlaste%@”,avAsset);
,它表明
dispatch_sync
正在导致死锁

但是我怎么才能找到原因呢?我不知道在哪里。assetCreationQueue是一个后台队列,我只能在主队列上对数组进行操作

编辑: 下面是一个更简单的测试,它也会失败:

[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
    if (asset){
        dispatch_async(weakSelf.assetCreationQueue, ^{
            if ([NSThread isMainThread]) {
                NSLog(@"already main thread"); // gets called often!!
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"dispatch async on main queue"); // never gets called!!
                });
            }
        });
     }
}];
所以我不明白的是:为什么我调用
dispatch\u async(weakSelf.assetCreationQueue
)却已经在主线程上了。这只会导致错误的结论:我创建的队列不是后台队列:

_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
为什么?

(这不是一个真正的答案,但我想提供足够的信息,以便继续前进。)

我尝试了一个非常简单的程序,但完全没有重现。我收到许多“在主队列上调度异步”的调用,而没有收到任何“已经主线程”的调用(与您期望的相反)。我正在iPhone 5上运行此程序。完整代码:

#import "AppDelegate.h"
@import AssetsLibrary;

@interface AppDelegate ()
@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

  ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
  AppDelegate __weak *weakSelf = self;
  [library enumerateGroupsWithTypes:ALAssetsGroupAll
                         usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
                           [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
                             if (asset){
                               dispatch_async(weakSelf.assetCreationQueue, ^{
                                 if ([NSThread isMainThread]) {
                                   NSLog(@"already main thread"); // gets called often!!
                                 } else {
                                   dispatch_async(dispatch_get_main_queue(), ^{
                                     NSLog(@"dispatch async on main queue"); // never gets called!!
                                   });
                                 }
                               });
                             }
                           }];
                         }
                       failureBlock:nil];
  return YES;
}
@end

请注意,在这里使用
weakSelf
是不必要的,也是危险的(但如果这是问题所在,您将崩溃)如果块将被存储在一个对象的属性中,你只需要<代码>弱自我>代码>保留。对于这样的短命块,它是不必要的或甚至是不需要的。你使用它的方式,如果拥有的对象在枚举的中间被释放(它可以),
weakSelf
将变为零,
weakSelf.assetCreationQueue
将变为
nil
,并且
dispatch\u async()
将崩溃。(但是,这不太可能是问题的原因,因为它应该崩溃。)

能否再次检查更新后的代码是否与实际测试的代码相同?缺少括号。@RobNapier修复了此问题。在我尝试在SO代码编辑器中格式化代码时,意外删除了此代码。这就是我正在测试的代码。您的后台队列和主队列似乎是一个整体。您是否可以注销
的漏洞elf.assetCreationQueue
返回
NSLog(@“%p”,queue);
以确认/反驳这一点?不幸的是,我对这段代码进行了大量的修改,因为它现在已经完全不同了。但AVAssetLibrary-EnumerateAssetSingBlock方法似乎正在使用dispatch_sync(dispatch_get_main_queue(),…)如果我尝试在主队列上调度块,这将导致死锁。谢谢Rob,但没有运气。在我的情况下,AVAssetLibrary枚举器调用的所有块都已在主线程上执行。我进行了更多测试,并从串行队列移到并发队列,并发现在调用dispatch_sync(mainQueue,…)时,它调用dispatch_async(concurrentQueue,…),它调用dispatch_async(mainQueue,…)或dispatch_sync(mainQueue,…),有一个死锁。你是说上面的代码本身在你的设备上不起作用?你运行的是什么版本的iOS?哦,现在我明白了!!!我不知道为什么或者如何,但突然之间你的代码在我的iPhone 5 iOS 7上完美地工作了。起初它似乎也在死锁中被挂起。我认为从我的原始代码来看,罪魁祸首是有一个问题意外成为调度同步的h\u异步调用。因为已经有主线程,调度同步将其锁定。谢谢x1000!接受您的回答。