Ошибка при попытке назначить __block ALAsset изнутри assetForURL: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.

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

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];
}

Вам следует retain а также autorelease the asset:

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

return [assetToReturn autorelease];
Другие вопросы по тегам