4

次のようなシリアル バックグラウンド キューを作成しています。

@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;

// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

次に、バックグラウンドで ALAsset オブジェクトを次のように列挙します。

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

assets 配列プロパティは次のように定義されます。

@property (nonatomic, strong) NSMutableArray *assets;

試してみると、コンソールにdispatch_sync(dispatch_get_main_queue(), ^{1 つしか表示されず、デッドロックが発生していることを示しています。NSLog(@"AVURLAsset %@", 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_async(weakSelf.assetCreationQueue. それは悪い結論につながるだけです:私が作成したキューはバックグラウンドキューではありません:

_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

なんで?

4

1 に答える 1

4

(本当の答えではありませんが、前進するのに十分な情報を提供したいと思います。)

非常に単純なプログラムを試しましたが、まったく再現しません。「メイン キューに非同期をディスパッチする」という呼び出しが多くあり、「既にメイン スレッド」という呼び出しはありません (予想とは逆です)。これを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するオブジェクトのプロパティにブロックを格納する場合にのみ必要です。selfこのような短命のブロックには必要ではなく、望ましくありません。あなたがそれを使用した方法は、所有しているオブジェクトが列挙の途中で割り当て解除された場合 (可能です)、weakSelfnil にweakSelf.assetCreationQueueなりnil、になり、dispatch_async()クラッシュします。(ただし、クラッシュするはずなので、これが問題の原因である可能性は低いです。)

于 2013-09-30T15:23:42.177 に答える