【问题标题】:Code stuck when requesting size property of ALAssetRepresentation请求 ALAssetRepresentation 的 size 属性时代码卡住
【发布时间】:2014-07-12 11:11:28
【问题描述】:

我有一个返回 ALAsset 数据的方法。我为一个又一个资产调用此方法,以从它们获取 NSData 并将其上传到服务器。每 4 或 5 次调用后,代码就会卡在下面的 rep.size 调用上。当我在 XCode 中暂停并恢复执行时,它又开始工作了。我完全被难住了,任何帮助将不胜感激。

澄清:死锁/代码卡在 ALAssetsLibrary 代码中,而不是我的代码中。

附加信息:我只有一个 ALAssetsLibrary 实例,我确保它没有被任何其他线程使用。

+(void)getDataFromAssetURL:(NSString *) myImageURL ofType:(enum ImageType)imageType andPerformBlock:(NSDataBlock)block blocking:(BOOL)blocking
{
    NSConditionLock * albumReadLock = nil;
    if (blocking) {

        albumReadLock = [[NSConditionLock alloc] initWithCondition:PENDING];
    }
    @autoreleasepool {
        @try {

            NSURL *str = [[NSURL alloc] initWithString: myImageURL];

            ALAsset * asset = [[AppManager sharedInstance].assetObjectCache objectForKey:str];
            if (asset && NO) {
                block( [Util getDataFromAsset:asset ofType:imageType] );

                // notifies the lock that "all tasks are finished"
                [albumReadLock lock];
                [albumReadLock unlockWithCondition:ALLFINISHED];
            }
            else {
                ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
                {
                    @autoreleasepool {
                        @try {


                            [[AppManager sharedInstance].assetObjectCache setObject:myasset forKey:str];

                            NSData * assetData = [Util getDataFromAsset:myasset ofType:imageType];

                            block(assetData);

                            // notifies the lock that "all tasks are finished"
                            [albumReadLock lock];
                            [albumReadLock unlockWithCondition:ALLFINISHED];



                        }
                        @catch (NSException *exception) {
                            block(nil);
                            // important: notifies lock that "all tasks finished" (even though they failed)
                            [albumReadLock lock];
                            [albumReadLock unlockWithCondition:ALLFINISHED];
                        }
                        @finally {

                        }
                    }

                };

                ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)
                {
                    block(nil);
                    // important: notifies lock that "all tasks finished" (even though they failed)
                    [albumReadLock lock];
                    [albumReadLock unlockWithCondition:ALLFINISHED];
                };



                if(str)
                {
                    NSURL *asseturl = str;

                    [[AppManager sharedInstance].assetslibrary assetForURL:asseturl
                                                               resultBlock:resultblock
                                                              failureBlock:failureblock];

                }
                else {
                    block(nil);
                    // notifies the lock that "all tasks are finished"
                    [albumReadLock lock];
                    [albumReadLock unlockWithCondition:ALLFINISHED];
                }
            }


        }
        @catch (NSException *exception) {
            block(nil);
            // notifies the lock that "all tasks are finished"
            [albumReadLock lock];
            [albumReadLock unlockWithCondition:ALLFINISHED];
        }
        @finally {

        }
    }

    if (blocking) {
        // non-busy wait for the asset read to finish (specifically until the condition is "all finished")
        [albumReadLock lockWhenCondition:ALLFINISHED];
        [albumReadLock unlock];

    }
}


+(NSData *)getDataFromAsset:(ALAsset *)myasset ofType:(enum ImageType)imageType
{
    ALAssetRepresentation *rep = [myasset defaultRepresentation];

    CGImageRef iref = nil;
    NSData *assetData = nil;

    if (imageType == FULL_RESOLUTION) {
        Byte *buffer = (Byte*)malloc(rep.size);
        NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
        assetData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
    }
    else if (imageType == LARGE_IMAGE){
        iref = [rep fullScreenImage];
        UIImage * uiimage = [UIImage imageWithCGImage:iref];
        ////NSLog(@"%f %f", uiimage.size.width, uiimage.size.height);
        assetData = UIImageJPEGRepresentation(uiimage, 1.0f);
    }
    else if (imageType == SQUARE_THUMB){
        iref = [myasset thumbnail];
        assetData = UIImageJPEGRepresentation([UIImage imageWithCGImage:iref], 1.0f);
    }
    else if (imageType == PERSPECTIVE_THUMB){
        iref = [myasset aspectRatioThumbnail];
        assetData = UIImageJPEGRepresentation([UIImage imageWithCGImage:iref], 1.0f);
    }

    return assetData;
}

新信息:原来问题仅出现在特定设备上。如上所述,如果我在 XCode 中暂停和取消暂停调试器,代码会向前移动。

【问题讨论】:

  • 我还不知道解决方案,但我知道这是一种非常神秘的方法。您需要研究 GCD,其主要目的之一是减少这种令人头疼的锁。它还使代码更加简洁,并且您获得了更高级别的抽象。此外,如果您在 ARC 环境中,则大部分情况下都不需要使用自动释放池。至于调试死锁,请参见:cocoabuilder.com/archive/xcode/324247-debugging-deadlocks.html
  • 就像我在问题中解释的那样,死锁来自 iOS(ALAssetLibrary 代码),而不是因为我使用的锁。我使用的锁只是为了使函数同步(不幸的是这是一个要求)。我正在更新问题以更清楚地表明死锁在 ALAssetLibrary 代码中。
  • @try/@catch 在 Objective-C 中的含义与在其他语言中不同。简单地说,它们只是一个更好的 Assert。您应该假设您的程序在捕获异常后处于损坏状态,唯一正确的做法是尽快中止程序。
  • 你解决了吗?
  • @Devfly 是和否。我用另一部 iPhone 测试了相同的代码,每次都运行良好,没有问题。然后,我尝试了另一个示例应用程序,该应用程序访问了第一部 iPhone 上的 ALAssetLibrary,并且每次都能重现该问题。那部 iPhone 的照片库似乎已损坏。我重置了那部 iPhone 并恢复了照片库,此后没有发现问题。

标签: ios iphone alassetslibrary alasset


【解决方案1】:

如果您从主线程启动此代码块,您将导致 ALAsset 代码中的死锁。您必须首先进入后台线程,因为 ALAssetsLibrary 需要能够在主线程上执行代码,如果主线程以您在上面概述的方式被阻塞,那么它将永远等待这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 2016-12-10
    • 2017-03-05
    • 1970-01-01
    • 2020-03-20
    • 1970-01-01
    相关资源
    最近更新 更多