在 assetForURL:resultBlock: 中尝试分配 __block ALAsset 时出错。

5
我正在尝试创建一个方法,以便为给定的资产url返回一个ALAsset。(我需要稍后上传该资源,并希望在结果块之外进行操作。)
+ (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;
}

问题是assetToReturn给出了一个EXC_BAD_ACCESS错误。
如果我尝试在块内部分配指针是否存在问题?我看过一些块的例子,但它们总是与像整数等简单类型一起使用。

1
在iOS(iPhone,iPad)上询问有关Cocoa Touch的问题时,请使用“cocoa-touch”标签。而“cocoa”标签则是用于与Mac OS X上的Cocoa相关的问题。 - user557219
1
顺便说一句:在这种情况下,retainCount 是无用的。 - bbum
2个回答

9
一些要点:
  1. 您必须保留创建ALAsset的ALAssetsLibrary实例,只要使用该资产。
  2. 您必须注册ALAssetsLibraryChangedNotification的观察者,当收到它时,您拥有的任何ALAsset和任何其他AssetsLibrary对象都需要重新获取,因为它们将不再有效。这可能随时发生。
  3. 不应该期望 -assetForURL:resultBlock:failureBlock:或带有failureBlock的任何AssetsLibrary方法是同步的。它们可能需要提示用户访问库,并且不会始终立即执行它们的块。最好将需要成功的操作放在成功块本身中。
  4. 仅当您绝对必须在应用程序中使此方法同步(我建议您不要这样做)时,您将需要在调用assetForURL:resultBlock:failureBlock:后等待信号量,并在阻止主线程时选择旋转runloop。

以下实现应在所有情况下作为同步调用满足要求,但实际上,您应该非常努力地使代码异步化。
- (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];
}

1

你应该 retainautorelease 这个资源:

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

return [assetToReturn autorelease];

1
这是绝对正确的。资产必须保留以在区块范围之外生存。 - bbum
在ARC下,这是不适用的。 - Elise van Looij
这是正确的,但在ARC下__block是strong而不是assign,因此编译器仍会添加缺失的retain。尽管如此,这个问题并没有使用ARC。 - hypercrypt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接