objective-c - iOS 11 - 核心数据 - UIColor 不再用作可转换属性

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

我使用可转换属性将颜色存储在我的二进制核心数据存储中,将属性的类指定为 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,这是我在 exanple 项目的 AppDelegate 中所做的,我在应用程序 didFinishLaunchingWithOptions 中添加对象,否则它是来自具有核心的 iOS11 应用程序的标准模板数据。加上一个小的数据模型和类。

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

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

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

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

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

编辑 1: 雷达编号:33895450

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

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

编辑 5:我之前注意到 UIColor 现在支持 NSSecureCoding,安全性是否应该以某种方式在其他类型的存储中被忽略。

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

编辑:Apple 就我的 TSI 回复了我,他们说正在调查中,没有解决方法,请等待错误。他们帮不上忙,退还了我的 TSI。

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

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

最佳答案

Apple 回复了我,有新的 persistentStore 选项!

我从苹果那里得到的文字:

/* Allows developers to provide an additional set of classes (which must implement NSSecureCoding) that should be used while decoding a binary store. Using this option is preferable to using NSBinaryStoreInsecureDecodingCompatibilityOption. */ COREDATA_EXTERN NSString * const NSBinaryStoreSecureDecodingClasses API_AVAILABLE(macosx(10.13),ios(11.0),tvos(11.0),watchos(4.0));

/* Indicate that the binary store should be decoded insecurely. This may be necessary if a store has metadata or transformable properties containing non-standard classes. If possible, developers should use the NSBinaryStoreSecureDecodingClasses option to specify the contained classes, allowing the binary store to to be securely decoded. Applications linked before the availability date will default to using this option. */ 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 项目

关于objective-c - iOS 11 - 核心数据 - UIColor 不再用作可转换属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46004942/

相关文章:

core-data - 实体 User 不符合键 "dateEndSubscription"的键值编码

objective-c - 在 NSFetchRequest 中按实体名称排序

ios - UIDatePicker事件已更改

iphone - 在 iPhone 上发送 UDP 数据包在新的 3G 连接上失败,但在其他情况下有效

iphone - UINavigationBar 背景图像通过drawRect - 提示动画问题

iphone - Photobucket iOS 集成

ios - 文本换行后 UITextView 大小不会改变大小,直到键入第二个字符

objective-c - 将操作连接到 Xcode 中的按钮

iphone - appendFormat 泄漏

objective-c - 如何组织具有循环依赖性的核心数据实体?