Как реализовать NSCoding для общего класса в Swift?

У меня возникают проблемы с родовыми классами и NSCoding в Swift (XCode beta 5). В частности, этот пример кода работает красиво:

class Foo : NSObject, NSCoding
{
    let bar: String
    init(bar: String){
        self.bar = bar;
    }

    func encodeWithCoder(aCoder: NSCoder!){
        aCoder.encodeObject(bar, forKey: "bar")
    }
    required init(coder aDecoder: NSCoder!){
        self.bar = aDecoder.decodeObjectForKey("bar") as String
        super.init()
    }


}

let foo = Foo(bar: "hello, world")

NSKeyedArchiver.archiveRootObject(foo, toFile: "test.dat")

Однако, когда я пытаюсь использовать тот же код и добавляю общий параметр; когда я пытаюсь закодировать объект; Я получаю ошибку "непризнанный селектор, отправленный в экземпляр":

class Foo<T> : NSObject, NSCoding
{
    let bar: String
    init(bar: String){
        self.bar = bar;
    }

    func encodeWithCoder(aCoder: NSCoder!){
        aCoder.encodeObject(bar, forKey: "bar")
    }
    required init(coder aDecoder: NSCoder!){
        self.bar = aDecoder.decodeObjectForKey("bar") as String
        super.init()
    }


}

let foo = Foo<String>(bar: "hello, world")

NSKeyedArchiver.archiveRootObject(foo, toFile: "test.dat")

Ниже приведены данные об ошибках и трассировка стека:

[_TtC6SafeId14ArrayContainer00007F8893418E58 encodeWithCoder:]: unrecognized selector sent to instance 0x7f8890ee4080
2014-08-16 10:43:54.632 SafeId[1778:32501] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtC6SafeId14ArrayContainer00007F8893418E58 encodeWithCoder:]: unrecognized selector sent to instance 0x7f8890ee4080'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000103a6e3e5 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010371e967 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000103a754fd -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x00000001039cd5ff ___forwarding___ + 495
    4   CoreFoundation                      0x00000001039cd388 _CF_forwarding_prep_0 + 120
    5   Foundation                          0x00000001032eaa75 _encodeObject + 1120
    6   Foundation                          0x0000000103326bf5 +[NSKeyedArchiver archiveRootObject:toFile:] + 241

Как я читал, Swift не может вызвать реализацию encodeWithCoder, когда класс является общим. Это верно? Как я могу обойти это, чтобы использовать NSCoding для общих классов?

Ответы

Ответ 1

В тот момент, когда вы делаете класс общим, он становится несовместимым с Objective-C. Все файлы NSCoder происходят в Objective-C, которые больше не могут обращаться к вашему классу.

Обратите внимание, что существуют и другие последствия создания общего класса. Например, если вы хотите добавить свойство описания, вы также получите ошибку времени компиляции:

// Error: '@objc' getter for non-'@objc' property
override var description: String {
    return "Foo: \(bar)"
}

Ответ 2

Существует довольно простой (хотя и уродливый) обходной путь для описания случая (не могу комментировать из-за требования к репутации, поэтому ставим его здесь). Нечто похожее может также работать как обходной путь для исходного вопроса:

class BaseNSObjectWithDescriptionFix: NSObject {
    func makeDescription() -> String {
        return super.description
    }

    override var description: String {
        return makeDescription()
    }
}

Теперь вы просто используете BaseNSObjectWithDescriptionFix вместо NSObject и переопределяете makeDescription по своему усмотрению.