问题
I am trying to create a method that will return me a ALAsset for a given asset url. (I need upload the asset later and want to do it outside the result block with the result.)
+ (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;
}
The problem is assetToReturn gives an EXC_BAD_ACCESS.
Is there some problem if I try to assign pointers from inside the block? I saw some examples of blocks but they are always with simple types like integers etc.
回答1:
A few things:
- You must keep the
ALAssetsLibrary
instance around that created theALAsset
for as long as you use the asset. - You must register an observer for the
ALAssetsLibraryChangedNotification
, when that is received anyALAsset
s you have and any other AssetsLibrary objects will need to be refetched as they will no longer be valid. This can happen at any time. - You shouldn't expect the
-assetForURL:resultBlock:failureBlock:
, or any of the AssetsLibrary methods with afailureBlock:
to be synchronous. They may need to prompt the user for access to the library and will not always have their blocks executed immediately. It's better to put actions that need to happen on success in the success block itself. - Only if you absolutely must make this method synchronous in your app (which I'd advise you to not do), you'll need to wait on a semaphore after calling
assetForURL:resultBlock:failureBlock:
and optionally spin the runloop if you end up blocking the main thread.
The following implementation should satisfy as a synchronous call under all situations, but really, you should try very hard to make your code asynchronous instead.
- (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];
}
回答2:
You should retain
and autorelease
the asset:
// ...
assetToReturn = [asset retain];
// ...
return [assetToReturn autorelease];
来源:https://stackoverflow.com/questions/7625402/error-trying-to-assigning-block-alasset-from-inside-assetforurlresultblock