Error trying to assigning __block ALAsset from inside assetForURL:resultBlock:

送分小仙女□ 提交于 2020-01-01 06:05:27

问题


I am trying to create a method that will return me a ALAsset for a given asset url. (I need upload the asset later and want to do it outside the result block with the result.)

+ (ALAsset*) assetForPhoto:(Photo*)photo
{
    ALAssetsLibrary* library = [[[ALAssetsLibrary alloc] init] autorelease];
    __block ALAsset* assetToReturn = nil;

    NSURL* url = [NSURL URLWithString:photo.assetUrl];
    NSLog(@"assetForPhoto: %@[", url);

    [library assetForURL:url resultBlock:^(ALAsset *asset) 
    {
        NSLog(@"asset: %@", asset);
        assetToReturn = asset;
        NSLog(@"asset: %@ %d", assetToReturn, [assetToReturn retainCount]);        

    } failureBlock:^(NSError *error) 
    {
        assetToReturn = nil;
    }];

    NSLog(@"assetForPhoto: %@]", url);
    NSLog(@"assetToReturn: %@", assetToReturn); // Invalid access exception coming here.

    return assetToReturn;
}

The problem is assetToReturn gives an EXC_BAD_ACCESS.

Is there some problem if I try to assign pointers from inside the block? I saw some examples of blocks but they are always with simple types like integers etc.


回答1:


A few things:

  1. You must keep the ALAssetsLibrary instance around that created the ALAsset for as long as you use the asset.
  2. You must register an observer for the ALAssetsLibraryChangedNotification, when that is received any ALAssets you have and any other AssetsLibrary objects will need to be refetched as they will no longer be valid. This can happen at any time.
  3. You shouldn't expect the -assetForURL:resultBlock:failureBlock:, or any of the AssetsLibrary methods with a failureBlock: to be synchronous. They may need to prompt the user for access to the library and will not always have their blocks executed immediately. It's better to put actions that need to happen on success in the success block itself.
  4. Only if you absolutely must make this method synchronous in your app (which I'd advise you to not do), you'll need to wait on a semaphore after calling assetForURL:resultBlock:failureBlock: and optionally spin the runloop if you end up blocking the main thread.

The following implementation should satisfy as a synchronous call under all situations, but really, you should try very hard to make your code asynchronous instead.

- (ALAsset *)assetForURL:(NSURL *)url {
    __block ALAsset *result = nil;
    __block NSError *assetError = nil;
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    [[self assetsLibrary] assetForURL:url resultBlock:^(ALAsset *asset) {
        result = [asset retain];
        dispatch_semaphore_signal(sema);
    } failureBlock:^(NSError *error) {
        assetError = [error retain];
        dispatch_semaphore_signal(sema);
    }];


    if ([NSThread isMainThread]) {
        while (!result && !assetError) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
    }
    else {
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }

    dispatch_release(sema);
    [assetError release];

    return [result autorelease];
}



回答2:


You should retain and autorelease the asset:

// ...
assetToReturn = [asset retain];
// ...

return [assetToReturn autorelease];


来源:https://stackoverflow.com/questions/7625402/error-trying-to-assigning-block-alasset-from-inside-assetforurlresultblock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!