Ошибка при попытке назначить __block ALAsset изнутри assetsForURL:resultBlock:

Я пытаюсь создать метод, который вернет мне 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.

Есть ли какая-то проблема, если я попытаюсь назначить указатели внутри блока? Я видел несколько примеров блоков, но они всегда с простыми типами, такими как целые числа и т. д.


person mithuntnt    schedule 02.10.2011    source источник
comment
Задавая вопрос о Cocoa Touch на iOS (iPhone, iPad), используйте тег «cocoa-touch». Тег «cocoa» предназначен для вопросов о Cocoa в Mac OS X.   -  person    schedule 02.10.2011
comment
Кстати: retainCount в этом контексте бесполезен.   -  person bbum    schedule 03.10.2011


Ответы (2)


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

  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];
}
person Ashley Clark    schedule 02.10.2011

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

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

return [assetToReturn autorelease];
person hypercrypt    schedule 02.10.2011
comment
Это определенно правильно. Актив должен быть сохранен, чтобы выжить за пределами блока. - person bbum; 03.10.2011
comment
Однако в рамках ARC это неприменимо. - person Elise van Looij; 06.01.2012
comment
Это верно, но в ARC __block является сильным, а не присваиваемым, поэтому компилятор все равно добавит отсутствующее сохранение. Однако этот вопрос не использует ARC. - person hypercrypt; 07.01.2012