Swift NSManagedObject子类在10.13及以下版本中错误地添加了模块名称 上下文
我正在使用Xcode 12在macOS Catalina(10.15.6)上构建一个应用程序。该应用程序使用核心数据。我已经让Xcode使用Swift生成了NSManagedObject实体的子类。这些看起来像这样:Swift NSManagedObject子类在10.13及以下版本中错误地添加了模块名称 上下文,swift,cocoa,core-data,nsmanagedobject,Swift,Cocoa,Core Data,Nsmanagedobject,我正在使用Xcode 12在macOS Catalina(10.15.6)上构建一个应用程序。该应用程序使用核心数据。我已经让Xcode使用Swift生成了NSManagedObject实体的子类。这些看起来像这样: // LPProject+CoreDataClass.swift import Foundation import CoreData @objc(LPProject) public class LPProject: NSManagedObject { @objc fun
// LPProject+CoreDataClass.swift
import Foundation
import CoreData
@objc(LPProject)
public class LPProject: NSManagedObject
{
@objc func printStuff() {
NSLog("This is a function to test the situation.")
}
}
和另一个自动生成的文件:
// LPProject+CoreDataProperties.swift
import Foundation
import CoreData
extension LPProject {
@nonobjc public class func fetchRequest() -> NSFetchRequest<LPProject> {
return NSFetchRequest<LPProject>(entityName: "LPProject")
}
}
在macOS 10.14.6和10.15上,这一点非常有效。在旧版本上,它会产生“无法识别的选择器”崩溃:
发生这种情况是因为模块名在类名前面。我看到这个日志消息:
warning: Unable to load class named 'MyApp.LPProject' for entity 'LPProject'.
Class not found, using default NSManagedObject instead.
根据我能找到的所有信息,在Core Data editor中正确配置了实体以使用@objc()
声明:
那么…我错过了什么?为什么在10.14.5及以下版本的类名前面加了模块名,而在10.14.6+版本中没有加模块名?我尝试了很多方法,包括基于其他SO答案的方法:
@obc(LPProject)
属性,改用@objc LPProject
。这修复了macOS 10.11->10.13,但破坏了10.14和10.15上的轻量级迁移
要点 macOS 10.14.6+上的核心数据解析
MyApp.LPProject
以查找类LPProject
,从而实例化正确的NSManagedObject子类。但是,在较旧的macOS版本上,核心数据无法连接这两个对象,因此它没有实例化LPProject
对象,而是返回到通用NSManagedObject
对象
因此,在macOS 10.14.5及以下版本中,我们必须告诉核心数据名为“LPProject”的实体的该死类名是LPProject
,而不是MyApp.LPProject
。我们必须对模型中的每个实体重复这一点。为此,我们可以劫持模型加载点:
- (NSManagedObjectModel *) managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"myDataFile" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
if (@available(macOS 10.14.6, *))
{
// Do nothing; not needed
}
else
{
NSArray<NSEntityDescription*> *entities = [_managedObjectModel entities];
NSMutableArray *fixedEntities = [NSMutableArray arrayWithCapacity:entities.count];
for (NSEntityDescription *des in entities)
{
if ([des.managedObjectClassName hasPrefix:@"MyApp."] && des.managedObjectClassName.length > 6)
{
NSString *fixedName = [des.managedObjectClassName substringFromIndex:6];
des.managedObjectClassName = fixedName;
}
[fixedEntities addObject:des];
}
[_managedObjectModel setEntities:fixedEntities];
}
return _managedObjectModel;
}
-(NSManagedObjectModel*)managedObjectModel
{
if(_managedObjectModel){
返回_managedObjectModel;
}
NSURL*modelURL=[[NSBundle mainBundle]URLForResource:@“myDataFile”,扩展名:@“momd”];
_managedObjectModel=[[NSManagedObjectModel alloc]initWithContentsOfURL:modelURL];
如果(@可用(macOS 10.14.6,*))
{
//什么也不做;不需要
}
其他的
{
NSArray*实体=[\u managedObjectModel实体];
NSMutableArray*fixedEntities=[NSMutableArray阵列容量:entities.count];
用于(N实体描述*实体中的des)
{
if([des.managedObjectClassName hasPrefix:@“MyApp.]”和&des.managedObjectClassName.length>6)
{
NSString*fixedName=[des.managedObjectClassName substringFromIndex:6];
des.managedObjectClassName=固定名称;
}
[fixedEntities addObject:des];
}
[\u managedObjectModel setEntities:fixedEntities];
}
返回_managedObjectModel;
}
结果 以上完全解决了这个问题。在旧的macOS版本上,Core Data现在可以正确地为每个实体找到正确的NSManagedObject子类。它也适用于现代macOS版本。轻量级迁移也在所有版本上继续工作 注意:此方法与NSManagedObject子类(由Xcode 12自动生成)中的
@objc(LPProject)
属性一起使用,并在Xcode中的核心数据模型编辑器GUI中将每个实体的“模块”设置为“当前产品模块”
注2:我不知道这是否明智。它具有丑陋的黑客的所有特征,但是(A)它可以工作,(B)它对大多数用户使用的macOS 10.14.6+没有任何改动
注3:如果使用此选项,请确保将
6
调整为模块名称中的字符数(包括句点)。否则,您将度过糟糕的一天。Update:在macOS 10.13上的已编译二进制文件上运行'otool-ov'命令表明定义了ObjC类和-printStuff方法。(这是有道理的,因为如果不是,崩溃也会发生在10.14和10.15上,编译器会抛出一个错误。)原因似乎是,出于某种原因,10.13上的核心数据没有返回正确的实体。注意:问题出现在macOS 10.14.5及以下版本上,因此我用正确的macOS版本更新了我的答案。
warning: Unable to load class named 'MyApp.LPProject' for entity 'LPProject'.
Class not found, using default NSManagedObject instead.
- (NSManagedObjectModel *) managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"myDataFile" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
if (@available(macOS 10.14.6, *))
{
// Do nothing; not needed
}
else
{
NSArray<NSEntityDescription*> *entities = [_managedObjectModel entities];
NSMutableArray *fixedEntities = [NSMutableArray arrayWithCapacity:entities.count];
for (NSEntityDescription *des in entities)
{
if ([des.managedObjectClassName hasPrefix:@"MyApp."] && des.managedObjectClassName.length > 6)
{
NSString *fixedName = [des.managedObjectClassName substringFromIndex:6];
des.managedObjectClassName = fixedName;
}
[fixedEntities addObject:des];
}
[_managedObjectModel setEntities:fixedEntities];
}
return _managedObjectModel;
}