PHAssetCollection fetchAssetCollectionsWithType崩溃问题

7
为了从设备上获取一些照片,以下方法被放置在默认全局队列中。如果系统版本大于iOS 8,则使用Photos框架,否则使用ALAssetLibrary
dispatch_semaphore_t sema = dispatch_semaphore_create(0);

dispatch_async(dispatch_get_global_queue(0, 0), ^{
   [self getPhotos:^(NSArray *photos) {
       dispatch_semaphore_signal(sema);
    }];
}

dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));

dispatch_semaphore_wait(sema, timeout);


- (void)getPhotos:(MyCallBack)callback {

  Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

   if (PHPhotoLibrary_class) {

    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];


    if (status != PHAuthorizationStatusAuthorized)
    {
        return;
    }

    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];

  }       
}

这个应用在64位设备上崩溃,比如iPhone 5S及更新的型号,堆栈跟踪如下。

Exception Type:  SIGTRAP
Exception Codes: TRAP_BRKPT at 0x190c41b60
Crashed Thread:  25

Thread 25 Crashed:
0   PhotoLibraryServices            0x0000000190c41b60 +[PLModelMigrator _validateCurrentModelVersionFailedWithNoVersionFromServer] :444 (in PhotoLibraryServices)
1   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
2   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
3   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
4   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
5   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
6   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
7   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
8   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
9   PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
10  PhotoLibraryServices            0x0000000190c41ef8 +[PLModelMigrator _validateCurrentModelVersionAttempt:] :316 (in PhotoLibraryServices)
11  PhotoLibraryServices            0x0000000190c43688 +[PLModelMigrator createDatabase] :508 (in PhotoLibraryServices)
12  PhotoLibraryServices            0x0000000190b7c1cc -[PLPhotoLibrary loadDatabase:] :808 (in PhotoLibraryServices)
13  PhotoLibraryServices            0x0000000190b71f70 -[PLPhotoLibrary initWithTransientContext:name:] :560 (in PhotoLibraryServices)
14  Photos                          0x0000000191722418 ___45-[PHPhotoLibrary backgroundQueuePhotoLibrary]_block_invoke :60 (in Photos)
15  libdispatch.dylib               0x0000000184bb21bc __dispatch_client_callout :16 (in libdispatch.dylib)
16  libdispatch.dylib               0x0000000184bb2fb0 _dispatch_once_f :56 (in libdispatch.dylib)
17  Photos                          0x00000001917223d4 -[PHPhotoLibrary backgroundQueuePhotoLibrary] :156 (in Photos)
18  Photos                          0x0000000191722604 -[PHPhotoLibrary photoLibraryForCurrentQueueQoS] :80 (in Photos)
19  Photos                          0x0000000191722578 -[PHPhotoLibrary managedObjectContextForCurrentQueueQoS] :24 (in Photos)
20  Photos                          0x00000001917611bc -[PHQuery _createFetchRequestIncludingBasePredicate:] :140 (in Photos)
21  Photos                          0x0000000191761c5c -[PHQuery fetchRequest] :52 (in Photos)
22  Photos                          0x0000000191783988 -[PHFetchResult initWithQuery:oids:registerIfNeeded:usingManagedObjectContext:] :376 (in Photos)
23  Photos                          0x0000000191783c2c -[PHFetchResult initWithQuery:] :100 (in Photos)
24  Photos                          0x0000000191763b5c -[PHQuery executeQuery] :52 (in Photos)
25  Photos                          0x00000001916c2998 ___67+[PHAssetCollection fetchAssetCollectionsWithType:subtype:options:]_block_invoke :72 (in Photos)
26  Photos                          0x000000019175fe48 +[PHObject authorizationAwareFetchResultWithOptions:fetchBlock:] :88 (in Photos)
27  Photos                          0x00000001916c2944 +[PHAssetCollection fetchAssetCollectionsWithType:subtype:options:] :84 (in Photos)

通常在32位设备上,例如iPhone5,iPhone4S,崩溃报告如下:
Exception Type:  SIGABRT
Exception Codes: #0 at 0x1b448ad4
Crashed Thread:  1
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException'  reason: 'CoreData: Cannot load NSManagedObjectModel.  nil is an illegal URL parameter'
Last Exception Backtrace:
0   CoreFoundation                  0x1bca0df7 ___exceptionPreprocess :131 (in CoreFoundation)
1   libobjc.A.dylib                 0x1af03077 _objc_exception_throw :39 (in libobjc.A.dylib)
2   CoreData                        0x1dc190c5 +[NSManagedObjectContext initialize] :1 (in CoreData)
3   PhotoLibraryServices            0x256a45d5 ___44+[PLManagedObjectContext managedObjectModel]_block_invoke :89 (in PhotoLibraryServices)
4   AssetsLibraryServices           0x24fc527d ___pl_dispatch_once_block_invoke :19 (in AssetsLibraryServices)
5   libdispatch.dylib               0x1b346083 __dispatch_client_callout :23 (in libdispatch.dylib)
6   libdispatch.dylib               0x1b346c65 _dispatch_once_f :43 (in libdispatch.dylib)
7   AssetsLibraryServices           0x24fc5267 _pl_dispatch_once :77 (in AssetsLibraryServices)
8   PhotoLibraryServices            0x256a456d +[PLManagedObjectContext managedObjectModel] :73 (in PhotoLibraryServices)
9   Photos                          0x26139475 +[PHQuery _relationshipForFetchType:predicate:] :257 (in Photos)
10  Photos                          0x2613a4cd -[PHQuery collectionFetchType] :85 (in Photos)
11  Photos                          0x261392b1 -[PHQuery copyWithZone:] :303 (in Photos)
12  Photos                          0x26158239 -[PHFetchResult initWithQuery:oids:registerIfNeeded:usingManagedObjectContext:] :125 (in Photos)
13  Photos                          0x26158567 -[PHFetchResult initWithQuery:] :81 (in Photos)
14  Photos                          0x2613c0cb -[PHQuery executeQuery] :53 (in Photos)
15  Photos                          0x260aaf4f ___67+[PHAssetCollection fetchAssetCollectionsWithType:subtype:options:]_block_invoke :73 (in Photos)
16  Photos                          0x2613898d +[PHObject authorizationAwareFetchResultWithOptions:fetchBlock:] :63 (in Photos)
17  Photos                          0x260aaf03 +[PHAssetCollection fetchAssetCollectionsWithType:subtype:options:] :97 (in Photos)

看起来是coredata的错误,我也不明白为什么最后的崩溃报告中同时存在AssetsLibraryServicesPhotoLibraryServices。 我尝试了很多方法,但无法复现崩溃,请帮忙解决。


你的plist文件中是否包含使用说明?(https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17) - danh
从32位崩溃来看,似乎是一个异常情况。添加一个异常断点,并在调试提示符中键入“po $arg1”。 - Léo Natan
你在应用程序生命周期的哪个阶段运行这段代码? - Aris
@LeoNatan,我不知道怎么做,我无法重现崩溃,该应用程序已经在应用商店上架,当应用程序崩溃时,报告将被上传。 - gabbler
如果您有报告,那么您应该在其中找到异常原因。 - Léo Natan
显示剩余6条评论
2个回答

3
这里有几个遗漏的地方,主要是你调用getPhotos需要上下文环境,并且我注意到你从未使用smartAlbums或回调函数。也许你只是因为认为它不相关而没有包含它。
无论如何,我创建了一个新项目(包括核心数据),这是我所做的每一步。没有崩溃:
1)在plist中添加了NSPhotoLibraryUsageDescription 2)在AppDelegate.h中添加了以下内容:
#import <Photos/Photos.h>

typedef void (^MyCallBack)(NSArray *photos);

3) 将 AppDelegate.mdidFinishLaunchingWithOptions 方法的默认实现替换为以下内容:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {

        if (status == PHAuthorizationStatusAuthorized) {
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                [self getPhotos:^(NSArray *photos) {
                    dispatch_semaphore_signal(sema);
                }];
            });
        }
    }];

    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));

    dispatch_semaphore_wait(sema, timeout);

    return YES;
}

- (void)getPhotos:(MyCallBack)callback {

    Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

    if (PHPhotoLibrary_class) {

        PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil];

        NSMutableArray *photos = [[NSMutableArray alloc] init];

        int i = 0;
        for (PHAssetCollection *assetCollection in smartAlbums)
        {
            i++;

            PHFetchResult *smartAlbum = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];

            int j = 0;
            for (PHAsset *asset in smartAlbum)
            {
                j++;

                [[PHImageManager defaultManager]
                 requestImageDataForAsset:asset
                 options:nil
                 resultHandler:^void(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {

                     [photos addObject:[UIImage imageWithData:imageData]];
                     if (i == [smartAlbums count] && j == [smartAlbum count]) {
                         callback([NSArray arrayWithArray:photos]);
                     }
                 }];

                if (i == [smartAlbums count] && j == [smartAlbum count]) {
                    return;
                }
            }
        }
    }
}

我和你做的事情类似,使用了smartAlbums。从崩溃报告来看,崩溃发生在fetchAssetsInAssetCollection这一行,之后的代码似乎没有被执行。因此我将其省略了,在崩溃时有30个线程正在运行。 - gabbler
正如你在其他地方所说的,这可能与CoreData有关,而不是资产获取。我们已经确定我的代码在真空中可以工作,这实际上只是你提供的代码进行了一些修改。如果你删除/注释掉调用getPhotos的语句(当然还有相关的dispatch_semaphore_wait),你是否仍会崩溃? - mmd1080
可能是因为Photos Framework是建立在CoreData之上的,用户捕获的图像保存在系统数据库中,有多个任务同时运行,并且我不是唯一一个可以访问Photos框架的人,可能是数据库被另一个线程占用,并且无法响应我的请求。该应用程序已经上架到应用商店,我无法重现崩溃,但是一些用户可以。 - gabbler
啊,我明白了。是的,你的第一优先事项应该是复现问题,如果你不这样做,就永远无法真正修复一个 bug。 - mmd1080
你成功重现了这个问题吗?这是一个无法解决的问题的高额赏金! - mmd1080
不幸的是,当应用程序启动时,我没有创建线程池。一些其他的SDK也使用了Photos框架并观察照片更改,我认为这可能是由于某些竞争条件引起的,或者当应用程序在后台模式下运行,例如从后台获取数据,然后应用程序突然变为活动状态,在此时请求照片可能会引起问题。 - gabbler

0
根据我的经验,在子线程中并不需要使用getPhotosfetchAssetCollectionsWithType。你可以试试看。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接