iOS 11 - 核心数据 - UIColor no longers作为可转换属性

Geo*_*own 10 xcode core-data objective-c ios11 macos-high-sierra

我使用可转换属性将颜色存储在我的二进制Core Data存储中,将属性的类指定为UIColor,如下所示:

#import "CoreDataEntity+CoreDataClass.h"
#import <UIKit/UIKit.h>


NS_ASSUME_NONNULL_BEGIN

@interface CoreDataEntity (CoreDataProperties)

+ (NSFetchRequest<CoreDataEntity *> *)fetchRequest;

@property (nullable, nonatomic, retain) UIColor *transformable;
@property (nullable, nonatomic, copy)   NSString *string;

@end

NS_ASSUME_NONNULL_END
Run Code Online (Sandbox Code Playgroud)

在iOS 11 Beta中,这已经停止了这样的错误:

NSUnderlyingException=value for key 'NS.objects' was of unexpected class 'UIColor'. Allowed classes are '{(\n    NSDecimalNumber,\n    NSData,\n    NSUUID,\n    NSNumber,\n    NSDate,\n    NSArray,\n    NSOrderedSet,\n    NSDictionaryMapNode,\n    NSString,\n    NSSet,\n    NSDictionary,\n    NSURL,\n    NSNull\n)}'.}";
    NSUnderlyingException = "Can't read binary data from file";
}
Run Code Online (Sandbox Code Playgroud)

我设法在GitHub上的XCode项目中复制特定问题(必须与XCode Beta一起运行两次以获得错误).

在示范项目的存储类型由NSPersistentStoreDescription控制,将其设置为NSBinaryStoreType,这是我在AppDelegate中的exanple项目做,我在应用程序didFinishLaunchingWithOptions添加对象,否则就从核心数据的iOS11应用程序的标准模板.加上一个小的数据模型和类.

如果你运行项目两次,第一次创建数据存储区,一切都很好.第二次,数据存储区尝试打开并崩溃应用程序.如果我使用SQL支持的数据存储区,这个问题似乎只与二进制数据存储区有关.但是,我的应用程序在野外并使用二进制.

我已经向苹果公司报告了它作为一个错误,并在开发者论坛上寻求帮助,但Apple没有承认这个错误,也没有任何帮助.

随着iOS11发布日期的临近而我没有解决方案,我有点担心,我的应用程序只能在iOS11中运行.

我已经尝试将属性更改为NSData并查看是否可以取消归档数据,但它似乎仍然以某种方式内部存储为UIColor,并且数据库无法打开.

任何人都可以看到解决方法吗?我有野外的应用程序,并可能推出更新以转换数据存储区,然后iOS11可以为一些人工作,但这并不能保证所有用户得到修复程序,他们可能会丢失他们的数据.

编辑1:雷达编号:33895450

编辑2:我刚刚发现,这适用于核心数据中的任何可转换属性,错误消息中支持的值只是默认属性类型.

编辑3:出于好奇,我填写了transformable属性的所有字段(以前从未需要).我将"NSKeyedUnarchiveFromData"添加到核心数据实体的值变换器名称中,它应该是默认值,但您永远不会知道.没有效果.无论如何它必须使用值变换器才能知道它是UIColor.我填写自定义类字段为UIColor,没有效果.

编辑5:我之前注意到UIColor现在支持NSSecureCoding,如果安全性在某种程度上是在键入的其他商店中被忽略的问题.

编辑:现在iOS已经发布,我已经使用了我的一个TSI来进一步升级.如果我必须使用一个来修复他们的软件,我会把它们还给我吗?

编辑:Apple在我的TSI上回复了我,他们说它正在调查中,没有解决方法,并等待bug.他们退还了我的TSI,因为他们无能为力.

编辑8:macOS High Sierra上的问题相同,使用NSColor而不是UIColor.

Apple仍未向我提供有关我的实际错误报告的任何反馈.

Geo*_*own 5

好吧,Apple回到我身边,有了新的persistentStore选项!

我从苹果公司得到的文字:

/ *允许开发人员提供在解码二进制存储区时应使用的其他类集(必须实现NSSecureCoding)。使用此选项比使用NSBinaryStoreInsecureDecodingCompatibilityOption更可取。* / COREDATA_EXTERN NSString * const NSBinaryStoreSecureDecodingClasses API_AVAILABLE(macosx(10.13),ios(11.0),tvos(11.0),watchos(4.0));

/ *表示二进制存储应该被不安全地解码。如果商店具有包含非标准类的元数据或可转换属性,则这可能是必需的。如果可能,开发人员应使用NSBinaryStoreSecureDecodingClasses选项指定所包含的类,以允许对二进制存储进行安全地解码。在可用日期之前链接的应用程序将默认使用此选项。* / COREDATA_EXTERN NSString * const NSBinaryStoreInsecureDecodingCompatibilityOption API_AVAILABLE(macosx(10.13),ios(11.0),tvos(11.0),watchos(4.0));

目前尚不清楚,但基本上,您必须在打开持久性存储时提供一个NSSet类,这些类用作可转换属性,并且符合NSSecureCoding的选项。

我的使用UIColor的示例:

NSError *localError;
NSDictionary *options;
if (@available(iOS 11.0, *)) {
    options = @{
                NSMigratePersistentStoresAutomaticallyOption : @YES,
                NSInferMappingModelAutomaticallyOption : @YES,
                NSBinaryStoreSecureDecodingClasses : [NSSet setWithObjects:[UIColor class], nil]
               };

} else {
    // Fallback on earlier versions
    options = @{
                NSMigratePersistentStoresAutomaticallyOption : @YES,
                NSInferMappingModelAutomaticallyOption : @YES,
                };
}
NSPersistentStore *newStore = [self.psc addPersistentStoreWithType:NSBinaryStoreType configuration:@"iOS" URL:psURL options:options error:&localError];
Run Code Online (Sandbox Code Playgroud)

编辑:为使用NSPersistentStoreDescription打开核心数据持久性存储的新方法添加了解决方案。该代码基于当前的核心数据模板。

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            NSURL *defaultURL = [NSPersistentContainer defaultDirectoryURL];
            defaultURL = [defaultURL URLByAppendingPathComponent:@"CoreDataTransformableAttribBug.binary"];
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataTransformableAttribBug"];
            NSPersistentStoreDescription *desc = [NSPersistentStoreDescription persistentStoreDescriptionWithURL:defaultURL];

            desc.type = NSBinaryStoreType;
            if (@available(iOS 11.0, *)) {
                [desc setOption:[NSSet setWithObjects:[UIColor class], nil] forKey:NSBinaryStoreSecureDecodingClasses];
            }
            _persistentContainer.persistentStoreDescriptions = @[desc];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Replace this implementation with code to handle the error appropriately.
                    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                    */
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                } else {
                    NSLog(@"Description = %@", storeDescription);
                }
            }];
        }
    }

    return _persistentContainer;
}
Run Code Online (Sandbox Code Playgroud)

我还用分支中的修复程序更新了gitHub项目