NSManagedObject 子类的 Swift 链接器错误实现具有所需初始化的协议

Posted

技术标签:

【中文标题】NSManagedObject 子类的 Swift 链接器错误实现具有所需初始化的协议【英文标题】:Swift linker error for NSManagedObject subclass implementing a protocol with a required init 【发布时间】:2015-03-18 23:06:58 【问题描述】:

我在开始的 Swift 项目中遇到问题。我使用 Alamofire 进行网络连接,使用 MagicalRecord 作为 Core Data 的包装器。我不知道这是否重要,但我还是要提一下。

设置

应用程序正在从 JSON API 检索其数据。使用Alamofire's Generic Response Object Serialization,我创建了Alamofire.Request 的扩展,如链接页面所示,并实现了以下协议:

@objc public protocol ResponseObjectSerializable 
    init?(response: NSHTTPURLResponse, representation: AnyObject)

我创建的类是这样的:

final class Foo: ResponseObjectSerializable 
    let bar: String
    let baz: String

    required init?(response: NSHTTPURLResponse, representation: AnyObject) 
        self.bar = representation.valueForKeyPath("bar") as String
        self.baz = representation.valueForKeyPath("baz") as String
    

要检索 JSON 数据并将其序列化为 Foo,我只需执行以下操作:

Alamofire.request(.GET, "http://foo.com/api").responseObject  (_, _, foo: Foo?, _) in
    println(foo.bar)

到目前为止,一切都很好。

问题:添加核心数据

当我想添加 Core Data 功能时,问题就开始了。我想使用上述方法从 API 中检索数据,对其进行序列化,并在某些时候使用 Core Data 将其保存。

所以,我决定将上面的Foo 类调整为:

@objc(Foo)
final class Foo: NSManagedObject, ResponseObjectSerializable 
    @NSManaged var bar: String
    @NSManaged var baz: String

    required init?(response: NSHTTPURLResponse, representation: AnyObject) 
        self.bar = representation.valueForKeyPath("bar") as String
        self.baz = representation.valueForKeyPath("baz") as String
    

这不起作用,因为 NSManagedObject 子类需要指定的初始化程序,所以我将我的类更改为:

@objc(Foo)
final class Foo: NSManagedObject, ResponseObjectSerializable 
    @NSManaged var bar: String
    @NSManaged var baz: String

    convenience required init?(response: NSHTTPURLResponse, representation: AnyObject) 
        let context = NSManagedObjectContext.defaultContext()
        let entity  = NSEntityDescription.entityForName("Foo", inManagedObjectContext: context)

        self.init(entity: entity!, insertIntoManagedObjectContext: context)

        self.bar = representation.valueForKeyPath("bar") as String
        self.baz = representation.valueForKeyPath("baz") as String
    

错误

该项目确实编译了,这看起来很有希望,但会引发以下错误:

架构 x86_64 的未定义符号: “__TFC7Project3Foo3barSS”,引用自:

TFC7Project3FoocfMS0_FT8responseCSo17NSHTTPURLResponse14representationPSs9AnyObject__GSqS0 在 Foo.o ld:未找到体系结构 x86_64 的符号 clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)

我不知道为什么会导致该错误,我真的很想摆脱它。那么,有谁知道如何解决这个问题?任何帮助表示赞赏。

我为什么要使用这种方法?

我的 ios 应用中有搜索功能(在模式中)。搜索结果被列为 Foo 实例,但我没有在本地保存它们中的任何一个(还)。这种方法只是在 UITableView 中轻松列出检索到的 JSON 数据的一种便捷方式。

一旦(从该列表中)选择了搜索结果,我想使用 Core Data 将该特定项目添加到本地数据库。只有那些已保存的项目才会在应用程序的主视图中列出。在我看来,使用相同的类似乎很方便。

我尝试过的(除了上面的)

在创建这个问题之前,我尝试在谷歌上搜索一些解决方案(但可以找到任何可行的答案)并使用此站点上的搜索功能。我发现了几个(有点)相关的问题:

根据this answer,我必须使用Xcode 6.3 beta,我已经尝试过(6D543q),但这也不起作用。我目前正在使用Xcode 6.2 (6C131e) btw。

另一个似乎相关的问题有以下answer。所以,我在变量声明中添加了dynamic@NSManaged dynamic var bar: String,但这也不起作用。

注意事项

我还想指出,我是 iOS(和 Swift)的新手,所以我可能没有使用正确的(或者我应该说是推荐的)方法。如果是这种情况,请告诉我,以便我采取其他可能不会导致此错误的方法。

【问题讨论】:

我正在尝试做同样的事情 - 我正在重构一些初始的 swift/核心数据工作 - 并试图将序列化代码移动到托管对象模型中(如 alamofire 文档中所述) 并在本 Wenderlich 教程中:raywenderlich.com/87595/intermediate-alamofire-tutorial 我想知道是否有更好的封装 - 保持核心数据模型干净 - 并使用由您的对象模型类组成的另一个对象,也符合 ResponseObjectSerializable... @DanielD 尽管我给出的答案对我有用,但它仍然是一个 hack 或者实际上是一个 临时 解决方法。我不太喜欢这种方法,但是一旦它在编译器中得到修复,我就可以删除*.c 文件并完成它。它在 Objective-C 中工作,所以它可能会被修复。无论如何,我一直在考虑另一种方法,使用两个对象。一个符合 ResponseObjectSerializable 协议,另一个是漂亮而干净的核心数据模型(就像你说的那样)。我只需要在保存之前将第一个的属性应用于后者。 【参考方案1】:

由于我是 Swift 新手,我没有意识到 __T 前缀字符串实际上是一个错位的 Swift 符号。当传递给swift-demangle 工具时,它转换为:

__TFC7Project3Foo3barSS ---> Project.Foo.bar.setter : Swift.String

这显然意味着 setter 方法不存在。根据this 的帖子,这是编译器中的一个错误。 @NSManaged 属性是完全动态的,因此对于托管属性根本不存在此函数,但 Swift 编译器的某些部分仍会生成使用它的代码。

同一篇文章描述了无论如何都不需要缺少的函数,因此我们可以通过向项目中添加一个简单的 C 文件来欺骗编译器:

void _TFC7Project3Foo3barSS() 

添加文件后,项目构建并运行良好,但它是一个hack。所以,这是一个解决方法,直到它真正在编译器中修复。

已报告为雷达here。

【讨论】:

以上是关于NSManagedObject 子类的 Swift 链接器错误实现具有所需初始化的协议的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 无法正确“创建 NSManagedObject 子类”Swift

NSManagedObject 子类在 swift 中初始化失败

将我的 NSManagedObject 子类与我的 ViewController.swift 连接起来

“filename+CoreDataProperties.swift”和“filename.swift”NSmanagedObject 子类有啥用?

swift NSManagedObject 子类初始化问题

如何在 swift 中将子类 NSManagedObject 分配给 var(而不是常量)