Ошибка при попытке назначить __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 ответа
Решение
Несколько вещей:
- Вы должны держать
ALAssetsLibrary
экземпляр вокруг которого создалALAsset
до тех пор, пока вы используете актив. - Вы должны зарегистрировать наблюдателя для
ALAssetsLibraryChangedNotification
когда полученоALAsset
Если у вас есть и любые другие объекты AssetsLibrary, вам нужно будет их повторно получить, поскольку они больше не будут действительными. Это может произойти в любое время. - Вы не должны ожидать
-assetForURL:resultBlock:failureBlock:
или любой из методов AssetsLibrary сfailureBlock:
быть синхронным. Им может потребоваться запросить у пользователя доступ к библиотеке, и не всегда их блоки выполняются немедленно. В самом блоке успеха лучше поместить действия, которые должны произойти в случае успеха. - Только если вам абсолютно необходимо сделать этот метод синхронным в вашем приложении (что я бы посоветовал вам не делать), вам потребуется подождать семафор после вызова
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];