NSMutableArray addObject in block 给出空数组



我有一个列表数组ALAsset URL。我想将该 URL 逐个转换为 ALAsset 并将其添加到新数组中。

这是我的代码:

-(void)retrieveAssetsWithArray:(NSArray *)assetsArray
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Background work
        __block NSMutableArray *retrievedAssetsArray = [[NSMutableArray alloc] init];
        for (int i = 0; i < [assetsArray count]; i++)
        {
            ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
            [library assetForURL:[NSURL URLWithString:[assetsArray objectAtIndex:i]]
                     resultBlock:^(ALAsset *asset)
             {
                 if (asset)
                 {
                     NSLog(@"assetss: %@", asset);
                     [retrievedAssetsArray addObject:asset];
                     NSLog(@"assets arayyyy: %@", retrievedAssetsArray);
                 }
             }
             failureBlock:^(NSError *error)
             {
                 NSLog(@"Error: Cannot load asset - %@", [error localizedDescription]);
             }
             ];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            // Update UI
            if ([self.delegate respondsToSelector:@selector(getRetrievedAssetsFromPhotoLibrary:)])
            {
                NSLog(@"retrievedAssetsArray :%@", retrievedAssetsArray);
                [self.delegate getRetrievedAssetsFromPhotoLibrary:retrievedAssetsArray];
            }
        });
    });
}

将 URL 转换为 ALAsset 的部分工作正常。但是当我尝试将其记录在dispatch_async(dispatch_get_main_queue()中时,retrievedAssetsArray会像这样返回:

retrievedAssetsArray :(
    "ALAsset - Type:Unknown, URLs:(null)",
    "ALAsset - Type:Unknown, URLs:(null)",
    "ALAsset - Type:Unknown, URLs:(null)",
    "ALAsset - Type:Unknown, URLs:(null)"
)

为什么会这样?任何人都可以告诉我如何解决这个问题?干杯。

我找到了问题的答案:

-(void)retrieveAssetsWithArray:(NSArray *)assetsArray
{
    __block NSMutableArray *retrievedAssetsArray = [[NSMutableArray alloc] init];
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    for (NSString *string in assetsArray) {
        dispatch_async(queue, ^{
            [library assetForURL:[NSURL URLWithString:string] resultBlock:^(ALAsset *asset) {
                [retrievedAssetsArray addObject:asset];
                dispatch_semaphore_signal(sema);
            } failureBlock:^(NSError *error) {
                dispatch_semaphore_signal(sema);
            }];
        });
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }
    dispatch_release(sema);
    /* Check out ALAssets and return in delegate methods*/    
    dispatch_async(dispatch_get_main_queue(), ^{
        // Update UI
        if ([self.delegate respondsToSelector:@selector(getRetrievedAssetsFromPhotoLibrary:)])
        {
            NSLog(@"retrieve %@", retrievedAssetsArray);
            [self.delegate getRetrievedAssetsFromPhotoLibrary:retrievedAssetsArray];
        }
    });
}

你应该像那里一样做:

typedef void (^VoidBlock)(void);
-(void)retrieveAssetsWithArray:(NSArray *)assetsArray
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Background work
        __block NSMutableArray *retrievedAssetsArray = [[NSMutableArray alloc] init];

        __block NSNumber *completCount = @0;
        VoidBlock internalCompletionBlock = ^{
            completCount = @(completCount.integerValue + 1);
            if (completCount.integerValue != assetsArray.count)
                return;
            dispatch_async(dispatch_get_main_queue(), ^{
                // Update UI
                if ([self.delegate respondsToSelector:@selector(getRetrievedAssetsFromPhotoLibrary:)])
                {
                    NSLog(@"retrievedAssetsArray :%@", retrievedAssetsArray);
                    [self.delegate getRetrievedAssetsFromPhotoLibrary:retrievedAssetsArray];
                }
            });
        };

        for (int i = 0; i < [assetsArray count]; i++)
        {
            ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
            [library assetForURL:[NSURL URLWithString:[assetsArray objectAtIndex:i]]
                     resultBlock:^(ALAsset *asset)
             {
                 if (asset)
                 {
                     NSLog(@"assetss: %@", asset);
                     [retrievedAssetsArray addObject:asset];
                     NSLog(@"assets arayyyy: %@", retrievedAssetsArray);
                 }
                 internalCompletionBlock();
             }
                    failureBlock:^(NSError *error)
             {
                 internalCompletionBlock();
                 NSLog(@"Error: Cannot load asset - %@", [error localizedDescription]);
             }
             ];
        }
    });
}

尽管 log 语句在任何结果块保证运行之前运行,但您的代码已经设法从外观上拉回了四个资产。

结果块是异步调用的,这意味着您可以在任何资产实际放入retrievedAssetArray之前轻松退出 for 循环。 然后,在主队列上执行 NSLog。

您需要重构代码以计算结果块的执行次数,并在计数达到assetsArray中的对象数时调度到主队列。 您需要在结果块执行此检查。

作为问题的一部分,您必须在整个异步assetForURL:resultBlock:failureBlock:期间保留ALAssetLibrary

因此,您可以考虑为 ALAssetLibrary 组织一个单一实例,例如:

static ALAssetsLibrary* library;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
});

主要问题是assetForURL:resultBlock:failureBlock:是异步方法,因此您需要等到它完成执行,无论是通过调用resultBlock:还是failureBlock:

您可以使用 dispatch_groupdispatch_semaphore 或一个简单的块来等待异步操作完成,该块减少异步操作的数量并在达到零时调用完成。

最新更新