【问题标题】:iOS 11 - Core Data - UIColor no longers works as transformable attributeiOS 11 - 核心数据 - UIColor 不再用作可转换属性
【发布时间】:2018-02-10 19:06:52
【问题描述】:

我使用可转换属性将颜色存储在我的二进制核心数据存储中,将属性的类指定为 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

在 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";
}

I managed to replicate the specific problem in an XCode project on GitHub(必须使用 XCode Beta 运行两次才能得到错误)。

在演示项目中,商店类型由 NSPersistentStoreDescription 控制并将其设置为 NSBinaryStoreType,我在示例项目的 AppDelegate 中执行此操作,并在应用程序 didFinishLaunchingWithOptions 中添加对象,否则它是来自具有核心的 iOS11 应用程序的标准模板数据。加上一个小型数据模型和类。

如果您运行该项目两次,第一次创建数据存储时一切正常。第二次,数据存储区尝试打开应用程序并使应用程序崩溃。据我所知,这个问题似乎只与二进制数据存储有关,如果我使用 SQL 支持的数据存储,它就可以工作。但是,我的应用程序在野外并使用二进制文件。

我已将它作为一个错误报告给 Apple,并在开发者论坛上寻求帮助,但 Apple 并未承认该错误,也没有任何帮助。

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

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

任何人都可以看到解决方法吗?我有这个应用程序,并且可能会在 iOS11 对某些人有效之前推出更新以转换数据存储,但这并不能保证所有用户都能得到修复,他们可能会丢失数据。

编辑 1: 雷达编号:33895450

编辑 2: 我突然想到这适用于核心数据中的 any 可转换属性,错误消息中支持的值只是默认属性类型。

编辑 3: 出于好奇,我填写了可转换属性的所有字段(以前从未要求过)。 我在核心数据实体的值转换器名称中添加了“NSKeyedUnarchiveFromData”,它应该是默认值,但你永远不知道。没有效果。无论如何,它必须使用值转换器才能知道它是 UIColor。 我把自定义类字段填为UIColor,没有效果。

编辑 5:我之前注意到 UIColor 现在支持 NSSecureCoding,如果安全性是在其他类型的商店中以某种方式被忽略的问题。

编辑:现在 iOS 已经发布,我使用了我的一个 TSI 来进一步升级。如果我必须使用一个来让他们修复他们的软件,我可以让他们回来吗?

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

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

Apple 仍然没有就我的实际错误报告给我任何反馈。

【问题讨论】:

  • 你能把你的雷达号码贴在这里吗?
  • 好的,我把它加到了问题的末尾
  • 我可以重现这个,但我现在想不出解决方法。
  • 是的。我认为最终 UIColor 被存储为 nsdata,核心数据处理编码,因为 uicolor 支持 NSCoding。我认为也许将属性设置为 NSData 可以让我对其进行解码,所以我很惊讶地看到它仍然无法打开商店,这意味着沿着这条线的某个地方,它打开了商店,知道它是一个 uicolor,然后停止了进度出于某种原因,甚至没有看课。
  • 谢谢,当我看不到原因时,我想我有时会发疯,但是这个错误不可能存在,对吧?无论存储什么,您都应该可以选择对其进行解码

标签: objective-c xcode core-data ios11 macos-high-sierra


【解决方案1】:

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];

编辑:为使用 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;
}

我还在分支中更新了我的 gitHub 项目

【讨论】:

  • 嗨乔治,我还有问题。 CoreData:错误:-addPersistentStoreWithType:二进制配置:(null) URL:file:///Users/klauskneupner/Library/Containers/de.kneupner.visualthinking.ibis/Data/Library/Autosave%20Information/Unsaved%20Visual%20Thinking% 20with%20IBIS%20Document.ibisVT 选项:{ NSBinaryStoreSecureDecodingClasses = ( NSColor ); NSPersistentStoreRemoveStoreOnCleanup = 1; } ...返回错误错误域= NSCocoaErrorDomain代码= 259“文件无法打开,因为它的格式不正确。”你什么时候调用它?
  • 我发现了问题。我需要使用 NSSet,即使是 Swift。将我的解决方案作为单独的答案发布。
  • 嗨,乔治。我现在遇到下一个问题。复制文件后,我遇到了同样的问题,但只有当我尝试显式保存时。自动保存似乎工作。很奇怪。你在iOS下有类似的问题吗?
  • 是否有编码选项?
  • 能否请您扩展解决方案的第二部分?您如何实际使用您的 persistentContainer?它位于何处,何时调用?非常感谢!
【解决方案2】:

乔治做了所有艰苦的工作。我只将它应用于 Swift。这是我的解决方案。我把它放到我的NSPersistentDocument 后代中。

override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    var options = storeOptions != nil ? storeOptions! : [String:Any]()
    if #available(OSX 10.13, *) {
        options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
    }
    options[NSMigratePersistentStoresAutomaticallyOption] = true
    options[NSInferMappingModelAutomaticallyOption] = true
    try super.configurePersistentStoreCoordinator(for: url, ofType: fileType, modelConfiguration: configuration, storeOptions: options)
}

现在我可以再次读取我的文件了。谢谢乔治!

【讨论】:

  • 谢谢!我对swift不太熟悉,swift Set 不工作吗?这些选项以 NS 为前缀,因此它没有前缀也就不足为奇了。但是令人讨厌的是很少的文档甚至没有提到要使用什么类型,我也会提出一个文档错误。核心数据文档似乎发生了很多变化,他们删除了很多信息,并且没有记录新内容。
  • 我不是 Swift 方面的专家,但我会说它不起作用。
  • 抱歉没有回来,忙于新工作。如果您开始一个包含核心数据的新项目,我只是从 xcode 模板中获得了第二个示例
  • 谢谢乔治。我现在发现在 macOS 上你不使用 NSPersistentContainer,而是使用 NSPersistentDocument。我目前的问题在这里描述:stackoverflow.com/questions/48699000/…
猜你喜欢
  • 2018-11-20
  • 1970-01-01
  • 2018-07-30
  • 2011-08-31
  • 1970-01-01
  • 2012-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多