尝试从assetForURL内部分配__block ALAsset时出错:resultBlock:

时间:2011-10-02 08:48:28

标签: cocoa-touch objective-c-blocks alasset

我正在尝试创建一个方法,它将返回给定资产网址的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。

如果我尝试从块内部指定指针,是否会出现问题?我看到了一些块的例子,但它们总是使用整数等简单类型。

2 个答案:

答案 0 :(得分:9)

一些事情:

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

  5. 以下实现应该满足所有情况下的同步调用,但实际上,您应该非常努力地使代码异步。

    - (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 :(得分:1)

您应该retainautorelease资产:

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

return [assetToReturn autorelease];