imstillalive
imstillalive

Reputation: 369

NSMutableArray addObject in block gives null array

I have an array of list ALAsset URL. I want to convert that URL into an ALAsset one by one and add it into a new array.

Here is my code:

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

The part to convert URL to ALAsset is working fine. But the retrievedAssetsArray returns like this when I try logging it in the dispatch_async(dispatch_get_main_queue():

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

Why is this happening? Can anyone please tell me how I can fix this? Cheers.

Upvotes: 0

Views: 690

Answers (4)

imstillalive
imstillalive

Reputation: 369

I found the answer to my question:

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

}

Upvotes: 1

pronebird
pronebird

Reputation: 12240

As a part of the problem, you have to retain ALAssetLibrary for the whole duration of asynchronous assetForURL:resultBlock:failureBlock:.

So you might consider to organize a singleton instance for ALAssetLibrary, for example:

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

The main problem is that assetForURL:resultBlock:failureBlock: is asynchronous method so you need to wait until it finishes execution whether by calling resultBlock: or failureBlock:.

You can wait for async operations to finish using dispatch_group or dispatch_semaphore or a simple block that decrements number of async operations and calls completion when it reaches zero.

Upvotes: 0

JeremyP
JeremyP

Reputation: 86651

Your code has managed to pull back four assets by the looks of things despite the fact that the log statement runs before any of the result blocks are guaranteed to have run.

The result block is invoked asynchronously, which means that you could easily come out of the for loop before any of the assets have actually been put in retrievedAssetArray. You then do th NSLog on the main queue.

You need to refactor the code to count the number of times the result block has been executed and do the dispatch to the main queue when the count has reached the number of objects in assetsArray. You need to do this check inside the result block.

Upvotes: 0

mityaika07
mityaika07

Reputation: 653

You should do it like there:

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

Upvotes: 0

Related Questions