Ошибка при попытке назначить __block ALAsset изнутри assetForURL: resultBlock: - PullRequest
5 голосов
/ 02 октября 2011

Я пытаюсь создать метод, который будет возвращать мне ALAsset для данного URL ресурса.(Мне нужно загрузить ресурс позже, и я хочу сделать это вне блока результатов с результатом.)

+ (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 ]

9 голосов
/ 03 октября 2011

Несколько вещей:

  1. Вы должны хранить ALAssetsLibrary экземпляр, вокруг которого был создан ALAsset, до тех пор, пока вы используете актив.
  2. Вы должны зарегистрировать наблюдателя для ALAssetsLibraryChangedNotification, когда он будет получен всеми имеющимися у вас ALAsset и любыми другими объектами AssetsLibrary, поскольку они больше не будут действительными.Это может произойти в любое время.
  3. Вы не должны ожидать, что -assetForURL:resultBlock:failureBlock: или любой из методов AssetsLibrary с failureBlock: будут синхронными.Им может потребоваться запросить у пользователя доступ к библиотеке, и не всегда их блоки выполняются немедленно.В самом блоке успеха лучше поместить действия, которые должны произойти в случае успеха.
  4. Только если вам абсолютно необходимо сделать этот метод синхронным в вашем приложении (что я бы посоветовал вам не делать), вы будетенужно ждать семафора после вызова assetForURL:resultBlock:failureBlock: и, при необходимости, вращать цикл запуска, если вы в конечном итоге блокируете основной поток.

Следующая реализация должна удовлетворять как синхронный вызов во всех ситуациях,но на самом деле вы должны очень стараться сделать свой код асинхронным.

- (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 голос
/ 02 октября 2011

Вы должны retain и autorelease актив:

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

return [assetToReturn autorelease];
...