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

Swift linker error for NSManagedObject subclass implementing a protocol with a required init

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

设置

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

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

我创建的 class 看起来像这样:

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 保存它。

所以,我决定把上面的Fooclass调整成这样:

@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 subclass 需要指定的初始化程序,所以我将 class 更改为:

@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
    }
}

错误

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

Undefined symbols for architecture x86_64:
"__TFC7Project3Foo3barSS", referenced from:

TFC7Project3FoocfMS0_FT8responseCSo17NSHTTPURLResponse14representationPSs9AnyObject__GSqS0 in Foo.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

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

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

我的 iOS 应用程序中有搜索功能(在模式中)。搜索结果列为 Foo 个实例,但我没有在本地保存任何一个(目前)。这种方法只是一种方便的方式,可以在 UITableView 中轻松列出检索到的 JSON 数据。

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

我试过的(除了上面的)

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

根据 this answer,我不得不使用 Xcode 6.3 beta,我试过 (6D543q),但也没有用。我目前正在使用 Xcode 6.2 (6C131e) 顺便说一句。

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

注释

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

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

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

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

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

void _TFC7Project3Foo3barSS() {}

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

它被报告为雷达here