Reputation: 95
I have an array with imageurls that corresponds to images in the assetslibrary, and I need to fetch all of them before I do a certain task. Whats the best way of doing this? Should i use the NSNotificationCenter
or would it better to use blocks, if so, any examples?
Here is the code I have:
- (IBAction)buttonClicked:(id)sender {
NSMutableArray* images = [NSMutableArray array];
//Need to loop through the takenImagesURLArray
for (NSURL *imageURL in takenImagesURLArray) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:imageURL
resultBlock:^(ALAsset *asset) {
if (asset) {
NSLog(@"HAS ASSET: %@", asset);
UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[images addObject:image];
} else {
NSLog(@"Something went wrong");
}
}
failureBlock:^(NSError *error) {
NSLog(@"Something went wrong, %@", error);
}];
}
//This will of course be called before images is ready
[self doCertainTaskWith: images];
}
Upvotes: 1
Views: 2243
Reputation: 3026
The block method you outlined will likely get to the call to do something with them before they are all fetched. You could have the call to process them in a completion handler actioned after the block action is complete.
I'll confess to getting confused with blocks myself. I use this to help.
In short I'd rearrange what you did to put the loop inside the block. Here's some syntactically terrible code to illustrate...
- (void)processImagesWithCompletionHandler:void (^)(NSArray*))completion {
dispatch_async(dispatch_get_global_queue, ^{ //runs in background thread
**for loop that gets images and adds to an array called images**
dispatch_async(dispatch_get_main_queue, ^{ //runs on main queue as it may affect the UI
completion(images);
});
});
}
You could then call this with:
[self processImagesWithCompletionHandler:^void (NSArray *images) {
self doCertainTaskWith:images];
}];
Upvotes: 0
Reputation: 3012
You could use Grand Central Dispatch for this.
But first, move the ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
outside the for-loop as it can be reused.
Now for the real code. It would look something like this:
dispatch_group_t dgroup = dispatch_group_create(); //0
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
for (NSURL *imageURL in urls) {
dispatch_group_enter(dgroup); // 1
[library assetForURL:imageURL resultBlock:^(ALAsset *asset) {
if (asset) {
NSLog(@"HAS ASSET: %@", asset);
UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[images addObject:image];
dispatch_group_leave(dgroup); //2
} else {
NSLog(@"Something went wrong");
dispatch_group_leave(dgroup);
}
} failureBlock:^(NSError *error) {
NSLog(@"Something went wrong, %@", error);
dispatch_group_leave(dgroup);
}];
}
dispatch_group_notify(dgroup, dispatch_get_main_queue(), ^{ //3
NSLog(@"Do something with %d images", [images count]);
});
Code explanation (Follow the comments from the code)
We create a group which will assist us in achieving what we want. Think of it as a simple list.
We enter each item into the group. Think of it as incrementing the retain
prop on an object, in this case, our group.
When we are done, we tell it to leave the group. Think of it as calling release
on an object.
When the count goes back to 0 on the group, all are tasks have finished. Whatever you need to do with the images, can be done in here.
Upvotes: 3
Reputation: 4705
Wain's answer in code
- (IBAction)buttonClicked:(id)sender {
__block NSInteger assetCount = 0;
NSMutableArray* images = [NSMutableArray array];
//Need to loop through the takenImagesURLArray
for (NSURL *imageURL in takenImagesURLArray) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:imageURL
resultBlock:^(ALAsset *asset) {
if (asset) {
NSLog(@"HAS ASSET: %@", asset);
UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[images addObject:image];
} else {
NSLog(@"Something went wrong");
}
assetCount++;
if (assetCount == takenImagesURLArray.count)
{
[self doCertainTaskWith:images];
}
}
failureBlock:^(NSError *error) {
NSLog(@"Something went wrong, %@", error);
}];
}
}
Upvotes: 0
Reputation: 119031
Use a simple counter, created outside the block and updated inside the block (on both success and failure). When the counter value matches the image count you know to execute the final statement (which should be inside an if
statement inside the assets library result block).
Upvotes: 0