0

I have the following:

    __block ALAssetsGroup* album = nil;
    ALAssetsLibrary* library = [ALAssetsLibrary new];
      [library enumerateGroupsWithTypes: ALAssetsGroupAlbum
                           usingBlock:
     ^ (ALAssetsGroup *group, BOOL *stop) {
         if (group) {
             NSString* title =
             [group valueForProperty: ALAssetsGroupPropertyName];
             if ([title isEqualToString: @"Test1"]) {
                 album = group;
                 *stop = YES;
                 NSLog(@"sd%@", album.description);
             }
         } else { // afterwards
            self.images = [self showFirstPhotoOfGroup:album]; // (oreilly, programming ios 7, ch.17)
            NSLog(@"A:%d",[_images count]);   // return photo count

             if (!album) {
                 NSLog(@"%@", @"failed to find album");
                 return;
             }


         }
     }
     ];

    NSLog(@"B:%d",[_images count]);

NSLog A: correctly displays the number of items in the album as 2.

NSLog B: displays 0. In fact, it gets processed before the block!

I understand why it does this - but this is the example that Apple has supplied. Please could you tell me how I can get _images to hold it's value outside the block. The block it seems has a completely different scope.

I've been trying to load an album into an array for ages - but this block example doesn't hold it's value outside the block!

user910028
  • 475
  • 4
  • 5

1 Answers1

1

The clue to the answer lies in your own observation - NSLog B is processed before NSLog A. The method enumerateGroupsWithTypes:usingBlock:failureBlock is documented as being asynchrnous, so it can return before anything has been enumerated. So your problem is nothing to do with values being retained etc., but simply that you are looking for the result before the work is done.

You have to design your application so that anything dependant on the enumeration is done after the enumeration has completed. The documentation for enumerateGroupsWithTypes states that after the enumeration is finished the block will be called once more and passed nil, you need to test for that and use it to trigger dependant actions.

HTH

CRD
  • 52,522
  • 5
  • 70
  • 86
  • I have seen the following: http://stackoverflow.com/questions/3586911/cocoa-thread-synchronisation-when-using-alassetslibrary-enumerategroupswithtype , and knew about asynchronous threads. But isn't the nill condition you mention is already present in the else block above? it runs 'NSLog(@"A:%d",[_images count]);' afterwards with the correct count. I thought maybe one way to add '[NSThread sleepForTimeInterval:20]; NSLog(@"B:%d",[__images count]);' after the block, but it didn't work. Is the only way to call another method, followed by another method? How can this block return it's value? – user910028 May 29 '14 at 20:10
  • It works! Having accepted that a block will always run after the calling outer function. The nil condition is actually present above..it's the else block. I just had to specify one additional method in the else block above: [_carousel reloadData]; and my carousel reloaded! If you're out there and loading an album into iCarousel, check oreilly:programming ios 7, ch.17..it can be done. – user910028 May 29 '14 at 20:45
  • Thank you x 10! You were right. I get it. One can only chain subsequent functions in the else block - you cannot return a value from a block, because it hasn't been processed yet. A block is not a method - a block is an isolated segment that is processed AFTER the calling function is complete. Subsequent actions needs to be specified in the else condition. I guess Apple had their reasons! – user910028 May 29 '14 at 21:00
  • @user910028 - "I guess Apple had their reasons!" - it is not Apple it is *asynchronous* code, same happens in any language. And blocks are just functions and can be called synchronously. HTH – CRD May 29 '14 at 21:11