Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them, it only takes a minute:

Part of my app has a photo browser, somewhat similar to Apple's Photos app, with an initial view controller to browse photo thumbnails and a detail view that's shown when you tap on a photo.

I'm using ALAssetsLibrary to access photos, and I pass an array of ALAsset URL's to my detail view controller so you can swipe from one photo to the next.

Everything works great, until I receive an ALAssetsLibraryChangedNotification while swiping from one photo to another (in the detail view controller), which often results in a crash:

NOTIFICATION: the asset library changed // my own NSLog for when the notification occurs

loading assets... // my own NSLog for when I start reloading assets in the thumbnail browser

Assertion failed: (size == bytesRead), function -[ALAssetRepresentation _imageData], file /SourceCache/AssetsLibrary/MobileSlideShow-1373.58.1/Sources/ALAssetRepresentation.m, line 224.

The specific line of code it crashes on, is in calling [currentRep metadata] as shown here:

- (void)someMethod {
        NSURL *assetURL = [self.assetURLsArray objectAtIndex:index];
        ALAsset *currentAsset;

        [self.assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {

            [self performSelectorInBackground:@selector(configureDetailViewForAsset:) withObject:asset];

            } failureBlock:^(NSError *error) {
                    NSLog(@"failed to retrieve asset: %@", error);
        }];
}

- (void)configureDetailViewForAsset:(ALAsset *)currentAsset {
    ALAssetRepresentation *currentRep = [currentAsset defaultRepresentation];

    if (currentAsset != nil) {
        // do some stuff
    }
    else {
        NSLog(@"ERROR: currentAsset is nil");
    }

    NSDictionary *metaDictionary;
    if (currentRep != nil) {
        metaDictionary = [currentRep metadata];

        // do some other stuff
    }
    else {
        NSLog(@"ERROR: currentRep is nil");
    }
}

I understand that once a notification is received, it invalidates any references to ALAsset and ALAssetRepresentation objects... but how am I supposed to deal with the situation where it invalidates something right in the middle of trying to access it?

I've tried setting a BOOL, right when receiving the notification to completely abort and prevent [currentRep metadata] from ever being called, but even that doesn't catch it every time:

if (self.receivedLibraryChangeNotification) {
    NSLog(@"received library change notification, need to abort");
}
else {
    metaDictionary = [currentRep metadata];
}

Is there anything I can do? At this point I'm almost ready to give up on using the ALAssetsLibrary framework.

(note this unresolved thread on the Apple dev forums describing the same issue: https://devforums.apple.com/message/604430 )

share|improve this question

1 Answer 1

up vote 5 down vote accepted
+50

It seems the problem is around here:

[self.assetsLibrary assetForURL:nextURL 

    resultBlock:^(ALAsset *asset) {
        // You should do some stuff with asset at this scope
        ALAssetRepresentation *currentRep = [asset defaultRepresentation];
        // Assume we have a property for that
        self.assetRepresentationMetadata = [currentRep metadata];
        ...
        // assume we have a method for that
        [self updateAssetDetailsView];
    } 

    failureBlock:^(NSError *error) {
        NSLog(@"failed to retrieve asset: %@", error);
    }];

Once you have got user asset it is better to copy asset information by providing necessary data to your details controller subviews or by caching for later use. It can be helpful for avoiding ALAsset invalidation troubles. When notification ALAssetsLibraryChangedNotification sent you may need to discard details controller and query the Library content from the beginning.

share|improve this answer
1  
I have to do more testing... but that may have helped. It's not shown in my code above (since I wanted to simplify it for posting here), but [currentRep metadata] was being called in a background thread. I changed that to instead retrieve the metadata dictionary inside the "assetForURL" resultBlock as you suggested, which happens to be on the main thread. It hasn't crashed so far... perhaps ALAssetRepresentation's -metadata method isn't thread safe? – Jawboxer May 27 '12 at 15:46
    
The most important thing here is that it takes time to get media with assetForURL. Therefore outside of completion block you cannot be sure you have media downloaded... – voromax May 27 '12 at 16:08
    
I updated my question to accurately show how I was calling the "metadata" method in a background thread. Based on your answer I've changed it so that I retrieve the metadata dictionary first, then pass both that and the asset to my configureDetailView method. Again, I need to do more testing, but so far so good... – Jawboxer May 27 '12 at 16:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.