Xcode 8 为 iOS 10 生成损坏的 NSManagedObject 子类

Xcode 8 generates broken NSManagedObject subclasses for iOS 10

我最近将我的 iOS 应用程序项目更新为 iOS 10。现在我正在尝试更改我的应用程序的核心数据模型,但是 Xcode 生成的新 NSManagedObject 子类坏了。我也尝试修复子类手册,但这不起作用。

核心数据模型的最低工具版本设置为 Xcode 7.0,代码生成语言设置为 Swift。

这是 Xcode 生成的代码:

import Foundation
import CoreData
import 

extension Group {

    @nonobjc public class func fetchRequest() -> NSFetchRequest {
        return NSFetchRequest(entityName: "Group");
    }

    @NSManaged public var name: String?
    @NSManaged public var platform: NSNumber?
    @NSManaged public var profiles: NSOrderedSet?

}

// MARK: Generated accessors for profiles
extension Group {

    @objc(insertObject:inProfilesAtIndex:)
    @NSManaged public func insertIntoProfiles(_ value: SavedProfile, at idx: Int)

    @objc(removeObjectFromProfilesAtIndex:)
    @NSManaged public func removeFromProfiles(at idx: Int)

    @objc(insertProfiles:atIndexes:)
    @NSManaged public func insertIntoProfiles(_ values: [SavedProfile], at indexes: NSIndexSet)

    @objc(removeProfilesAtIndexes:)
    @NSManaged public func removeFromProfiles(at indexes: NSIndexSet)

    @objc(replaceObjectInProfilesAtIndex:withObject:)
    @NSManaged public func replaceProfiles(at idx: Int, with value: SavedProfile)

    @objc(replaceProfilesAtIndexes:withProfiles:)
    @NSManaged public func replaceProfiles(at indexes: NSIndexSet, with values: [SavedProfile])

    @objc(addProfilesObject:)
    @NSManaged public func addToProfiles(_ value: SavedProfile)

    @objc(removeProfilesObject:)
    @NSManaged public func removeFromProfiles(_ value: SavedProfile)

    @objc(addProfiles:)
    @NSManaged public func addToProfiles(_ values: NSOrderedSet)

    @objc(removeProfiles:)
    @NSManaged public func removeFromProfiles(_ values: NSOrderedSet)

}

编辑:这些是 Xcode 给出的特定错误:

1. Group+CoreDataProperties.swift:13:1: Expected identifier in import declaration (the empty import)
2. Group+CoreDataProperties.swift:13:11: 'Group' is ambiguous for type lookup in this context
3. Group+CoreDataProperties.swift:15:16: Cannot specialize non-generic type 'NSFetchRequest'
4. Group+CoreDataProperties.swift:26:11: 'Group' is ambiguous for type lookup in this context
4. Group+CoreDataProperties.swift:43:82: 'SavedProfile' is ambiguous for type lookup in this context

我发现这个 在某些方面很有帮助。 如果我按照大卫描述的方式生成代码。我只是得到了一些语法错误的代码,如果我修复了错误,一切都会像以前一样工作。没有丢失以“.”开头的文件没有了。

我必须纠正的语法错误是 - 不必要的进口 - 还有一些 public 属性在我的代码

中不打算成为 public

我终于开始工作了。这是我所做的。 (航班是我的实体之一)

我按如下方式设置 xcdatamodeld

然后实体为

然后我使用编辑器 -> 创建 NSManagedObject 子类

这会为我的航班实体创建两个文件

机票+CoreDataProperties.swift

机票+CoreDataClass.swift

我将 Flights+CoreDataClass.swift 重命名为 Flights.swift

Flights.swift 就是

import Foundation
import CoreData

@objc(Flights)
public class Flights: NSManagedObject {

}

航班+CoreDataProperties.swift是

import Foundation
import CoreData


extension Flights {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Flights> {
        return NSFetchRequest<Flights>(entityName: "Flights");
    }

    @NSManaged public var ...
}

这似乎适用于 me.I 无法让 Codegen 以任何其他方式工作,即使我尝试了那里的许多建议。

这也让我摸不着头脑,我把它作为一个辅助。不要忘记使用 FetchRequest 的新泛型版本,您可以做到这一点

let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Flights")

删除第 3 个 import 语句,因为它是空的。

注意:我不知道为什么会这样,但我猜这是Xcode8中的一个错误。只需删除它就可以正常工作。

我是 运行 最新的 Xcode 8.1 beta (8T47) 版本。

根据错误日志(见下文),创建了两个自动生成文件的副本。一个副本放在您的 Xcode 项目文件夹中(在 Xcode 左侧工具栏上您的目录中可见的那个),第二个副本在您的 DerivedData 文件夹中为您的项目创建:

/Users/<your name>/Library/Developer/Xcode/DerivedData/<your app>-axhtnodotznxnrenefflktnxfeal/Build/Intermediates/<your app>.build/Debug-iphonesimulator/<your app>.build/DerivedSources/CoreDataGenerated/<your app>/<class name>+CoreDataProperties.swift
/Users/<your name>/Library/Developer/Xcode/DerivedData/<your app>-axhtnodotznxnrenefflktnxfeal/Build/Intermediates/<your app>.build/Debug-iphonesimulator/<your app>.build/DerivedSources/CoreDataGenerated/<your app>/<class name>+CoreDataClass.swift

删除 Xcode 项目目录中的副本将解决所有问题,但事实证明毫无意义,因为您无法再编辑这些文件...

我不会说这是一个解决方案,而只是让项目构建的半途而废的方法。希望这个错误能尽快得到解决。

您需要做的是在执行创建 NSManagedObject 子类之前将 CodeGen 设置为 Category/Extension。

我相信您在生成 NSManagedObject 子类文件后遇到这些错误的原因可能与您在更改数据模型时选择的 Codegen 选项有关。

我不认为这是一个错误。至少在 Xcode 8.1 (8B62).

中没有

我遇到了类似的问题并通过将 Codegen 选项更改为 "Manual/None" 并将模块选项保留为 "Global Namespace" 来修复它。然后我生成了我的 NSManagedObject 子类文件。但是,根据您的情况,您甚至可能不需要生成 NSManagedObject 子类。

下面是基于我对 Apple 文档、Apple 开发者论坛和我自己的项目测试的审查,关于实体 Codegen 选项的更多详细信息。

选项 1:"Class Definition"

  • 将此视为 "plug and play" 选项

  • 使用模型编辑器创建实体和相关属性 您希望 Core Data 管理的内容。

  • 不要使用 NSMangagedObject 子类生成器。所需的 文件由 Xcode 自动为您生成(但是它们确实 不会显示在您的项目导航器中)。

  • 如果您确实生成了 NSManagedObject 文件,Apple 会告诉您 这些文件不应在 said 的 header 评论中编辑 文件。如果您确实需要编辑这些文件,那么这是个好兆头 您需要使用另一个 Codegen 选项。

  • 保留实体的默认 Class 选项(模块 = 全局 命名空间和代码生成 = Class 定义)

  • 如果你需要覆盖NSManagedObject的方法,你可以 使用 Class 选项下的名称创建扩展。

    // Optional extension in Entity.swift extension Entity { // Override NSManagedObject methods }

选项 2:"Manual/None"

  • 与选项 1 类似,只是您可以查看和编辑 NSManagedObject 子类文件。

  • 使用模型编辑器创建实体和相关属性 您希望 Core Data 管理的内容。

  • 在使用 NSManagedObject 子类生成器之前,设置 Codgen 实体的 "Manual/None" 选项。模块选项应该是 全局命名空间。

  • 在你add/remove/update一个属性之后,你有两个选择:(1) 手动更新为您生成的 NSManagedObject 文件 或者 (2) 再次使用 NSManagedObject 子类生成器(这将 仅更新 Entity+CoreDataProperties.swift 文件)。

  • 同样,如果您需要重写 NSManagedObject 中的方法,您 可以创建一个扩展。扩展应该创建在 实体+CoreDataClass.swift 文件而不是 Entity+CoreDataProperties.swift 文件(该文件可能因 模型编辑器更改)。

    // Optional extension in Entity+CoreDataClass.swift extension Entity { // Override NSManagedObject methods }

选项 3:"Category/Extension"

  • 如果您有不需要的自定义属性,请使用此选项 由核心数据管理。

  • 使用模型编辑器创建实体和相关属性 您希望 Core Data 管理的内容。

  • 确保 Module 选项设置为 Current Product Module 并且 Codegen 选项设置为 "Category/Extension".

  • 使用您设置的属性创建您自己的 NSManagedObject 子类 将自行管理。

  • 不要使用 NSMangagedObject 子类生成器。所需的 文件由 Xcode 自动为您生成(但是它们确实 不会显示在您的项目导航器中)。

    // Subclass NSManagedObject in Entity.swift class Entity: NSManagedObject { // Self managed properties }

    // Optional extension in Entity.swift extension Entity { // Override NSManagedObject methods }

我认为这是一个 xcode 问题,其中 xcode 为核心数据类别 class 生成了错误的语法。 我正在为一个实体 AgentDetails 创建一个 nsmanageobjectsubclass

这里 xcode 在 AgentDetails+CoreDataClass.hAgentDetails+CoreDataClass.m 中创建了错误的代码结构。它们的代码结构如下:

所以它有重复的接口问题,因为 AgentDetails.h 有相同的接口。

现在要解决此问题,您必须更改 AgentDetails+CoreDataClass.h

中的代码

AgentDetails+CoreDataClass.m像这样:

尝试设置 CodeGen:Manual/None 模块:当前产品模块

当我遇到同样的问题时,它对我很有效。

Xcode 8.1 似乎在内部生成模型 Class。只需删除创建的 2 类,您仍然可以在代码中使用这些实体。

这是我收到的错误消息

<unknown>:0: error: filename "Recipe+CoreDataProperties.swift" used twice: '/Users/Rick/Desktop/Devslop/Rick Recipe/Recipe+CoreDataProperties.swift' and '/Users/Rick/Library/Developer/Xcode/DerivedData/Rick_Recipe-cctgjudvqobxlwetbcwmzrgxigwg/Build/Intermediates/Rick Recipe.build/Debug-iphonesimulator/Rick Recipe.build/DerivedSources/CoreDataGenerated/Rick_Recipe/Recipe+CoreDataProperties.swift'
<unknown>:0: note: filenames are used to distinguish private declarations with the same name
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1

另外,如果您自己添加NSManagedObject子类,请不要忘记按照以下步骤操作: 转到 '.xcdatamodeld' -> 选择实体 -> 显示数据模型检查器 -> Codegen -> 选择 'Manual/None'

这对我有用。

Xcode 8.2.1,我做到了:(感谢 Daniel Chepenko

但是Xcode 8.2.1会在生成的文件中添加一个愚蠢的.:

只需删除 . 即可 运行 正确。

如果您有旧版应用程序并且希望继续手动添加托管对象类

  1. 确保您的核心数据模型 codegen 设置为 Manual/None
  2. Select 你的核心数据模型文件
  3. 创建托管对象子类

在数据模型检查器 select 'Module' 下的当前产品模块和 'Codegen' 下的 Manual/None。 (这允许您编辑子类文件并在修改同一实体下的属性时重新生成 CoreDataProperties.swift。)

然后您可以在Editor 菜单下选择Create NSManagedObjectSubclass。 Xcode 将为您创建 2 个文件 YourEntity+CoreDataClass.swift 和 YourEntity+CoreDataProperties.swift。请注意,如果您没有最新的 Xcode (8.2.1),您在模型检查器中设置的任何 optional/nonoptional 属性都不会正确显示。你可以编辑 你的实体 +CoreDataProperties.swift 文件。

  1. 要解决此问题,请删除应用程序的派生数据。

  2. 然后将 Module 更改为 Current Product Module 并在 CodegenManual/None.

我有一个航班实体,数据模型检查器中的这些设置对我有用:

  1. 将名称设置为空(您会看到 "NSManagedObject" 的一些灰色文本)。默认将写为 "Flights" 只需将其删除。

  2. 模块中没有任何内容。"Global Namespace" 您将看到灰色的内容。

  3. Codegen 到 Manual/None.

  4. 然后转到编辑器 -> 创建 NSManagedObject 子类以创建 swift 文件

  5. 将 Flights+CoreDataClass.swift 重命名为 Flights.swift

这是屏幕截图的 link(由于某些原因,imgur 拒绝了它:( ) https://drive.google.com/file/d/0B8a3rG93GTRedkhvbWc5Ujl4Unc/view?usp=sharing

我改名后如下:

来自:YourEntity+CoreDataClass.swift 收件人:YourEntity.swift

来自:YourEntity+CoreDataProperties.swift 致:你的实体CoreDataProperties.swift

然后就可以了。问题似乎是由“+”引起的。

您可以尝试这些步骤

  1. Select Xocde Project Navigator 中的 .xcdatamodeld 文件
  2. Data Modul Inspector 中,确保 Codegen : Manual/None
  3. 然后编辑 -> 创建NSManagedObject子class
  4. 清理产品并重建

这是工作吗?如果没有

确保你们 classEntities 的名称不同。您可以在 Build Phases -> Compile Sources 中找到这些 class。重命名 class 或重复的实体

或者

您可以运行在您的项目文件夹中使用此命令来获取更多错误信息

xcodebuild -verbose

遇到这个问题时我收到了这条消息

duplicate symbol _OBJC_CLASS_$_RandomList in:

xxxx/RandomList.o

xxxx/RandomList+CoreDataClass.o

RandomList.hRandomList+CoreDataClass.h编译时符号相同

我想为什么Xcode没有警告你,但编译器会抛出错误

希望对您有所帮助

对我来说,我通过手动继承 NSManagedObject 创建了 CocoaTouch 类。我将模块设置为 Current Product Module,将 Codegen 设置为 Category/Extension,它运行良好。

我刚在我的模型中将一个 Person 实体添加到 Core Data 模板时遇到了问题。然后为此生成 NSManagedObject 子类,它不会编译,给我一个链接器错误。结果我应该将 Codegen 选项更改为 Manual/None 以使其工作(参见图片的右下部分)。

在我的特殊情况下,我已将 CoreData 添加到现有项目中,该项目已有一个与我的实体同名的模型 class。删除旧模型 class 解决了这个问题,因为它的类型与新的 CoreData 实体 class.

发生冲突

鉴于我可以看到很多解决问题的答案,所以我有点怀疑,但是 缺乏这里真正发生的事情背后的真正原因。

我偶然发现了同样的问题,通过快速研究我发现:

  • 直到 Xcode7,Core Data 实体的默认行为是开发人员必须手动创建和维护相应 NSManagedObject 子类的更改。 (这可以并且仍然可以通过使用编辑器菜单选择 "Create NSManagedObject subclass.." 选项来完成)。

  • 从 Xcode8 开始,Apple 在 Data Model Inspector 中包含了一项名为 Codegen 的新设置,用于管理和维护 NSManagedObject 子类.此设置的默认值是 "Class Definition",它告诉 Xcode 在编译时根据我们定义的实体生成 NSManagedObject 子类,并将它们放在派生数据文件夹中。

在项目中拥有我们的 NSManagedObject 子类 + Xcode 自动生成的子类让我们找到 "Multiple commands produce..." 编译时错误背后的真正原因,因为我们现在有重复的托管对象模型!!!

然后解决方案是使用一个或另一个,而不是两个!我个人更喜欢在项目中使用我的托管对象模型,因此我将此 "Codegen" 设置更改为 "Manual/None"。如果您不向模型添加逻辑,我敢打赌您可以选择删除项目的托管对象模型,并让 Xcode 根据上述设置的 "Class Definition" 值执行其操作。这才是处理这个问题的正确方法。

您可以在此处找到一篇详细解释所有这些的精彩文章:

https://medium.com/@kahseng.lee123/core-data-codegen-explained-462c30341041

我按照下面给出的例子解决了它

创建CurrentcyPair.xcdatamodeld

select CurrentcyPair.xcdatamodeld and select Default -> Entity (CurrencyPair) ,重命名 class 作为 (.ManagedCurrencyPair)

Select 实体并打开 Inspector。将数据模型的代码生成设置为 Manual/None