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 子类有啥用?