2017-10-30 19 views
1

스위프트 4에서 코드 가능을 사용하여 코드를 업그레이드하고 줄이려고하면 클래스/참조 유형 동작과이 동작이 어떻게 관련되는지 이해하기가 쉽지 않습니다.참조 유형/서브 클래 싱 및 변경 사항 Swift 4 코드 가능 및 인코더/디코더

저는 두 가지 클래스가 있습니다 - 모든 데이터가 저장되는 SuperClass와 UserDefaults (장소 이름은 좌표가있는 & 문자열)에 저장되며, 필요하지 않은 추가 임시 정보가 포함 된 SubClass (SuperClass 좌표의 날씨 데이터).

는 스위프트 (3)에서이 같은 데이터를 저장하는데 사용 :

func saveUserDefaults() { 
    var superClassArray = [SuperClass]() 
    // subClassArray is of type [SubClass] and contains more data per element. 
    superClassArray = subClassArray 
    let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray) 
    UserDefaults.standard.set(superClassData, forKey: " superClassData") 
} 

슈퍼 또한 필요한 초기화 디코더 & 인 코드 기능을 포함 NSObject의 & NSCoding 을 따른다. 모두 정상적으로 작동했습니다.

스위프트 4로 바꾸려고 시도 할 때 & 코드 가능 코드를 적용하기 위해 SuperClass를 수정했습니다. SuperClass에는 이제 기본 이니셜 라이저가 하나만 있으며 Swift 3의 엔코더/디코더는 없습니다.이 새로운 접근 방식에서는 KeyedArchiving이 발생하지 않습니다 (아래). 하위 클래스는 변경되지 않습니다. 불행히도 내가 시도하는 라인에서 충돌이 발생합니까? encoder.encode [스레드 1에게 : EXC_BAD_ACCESS (코드 = 1, 주소 = 0x10)]. 내 가정은 하나의 SuperClass 및 하나의 SubClass (subClassArray [0] === superClassArray [0]이 참인)와 동일한 참조 유형으로 인코더가 혼동되고 있다는 것입니다.

func saveUserDefaults() { 
    var superClassArray = [SuperClass]() 
    superClassArray = subClassArray 
    // assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded 
    let encoder = JSONEncoder() 
    if let encoded = try? encoder.encode(superClassArray){ 
     UserDefaults.standard.set(encoded, forKey: " superClassArray ") 
    } else { 
     print("Save didn't work!") 
    } 
} 

을 그리고, 대신 다음, 빈 superClassArray를 작성, 사용의 :

let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)} 

: 위의 그림과 같이 이 superClassArray = subClassArray, 나는 한 줄이 교체 나는이 작업을 거라고 생각 이 작동합니다. 다시 말하지만, 클래스 참조 유형 & 내부의 값을 전달하고 있기 때문에 가정은 superClassArray = subClassArray를 만들지 않았기 때문입니다. 또한, 예상대로, subClassArray [0] === superClassArray [0]은 거짓입니다.

Swipe 3에서 "old stuff"는 왜 사용 했습니까? 전에 superClassArray = subClassArray 줄을 사용했는데 왜 그렇게 했습니까? superClassData = NSKeyedArchiver .archivedData (withRootObject : superClassArray) ? 이전 Swift 3 인코더/디코더에서 발생했던 Swift 4에서 어레이를 생성하여 동일한 결과를 얻었습니까? 루핑/레크리에이션입니다

고마워요!

+0

'JSONEncoder'에 의해 던져진 오류를 잡아서 출력하십시오. 인코딩이 실패하는 이유를 더 잘 이해할 수 있습니다. 또한 클래스의 정의 (또는 문제를 설명하는 단순화 된 버전)를 포함 할 수 있다면 누군가가 당신을 도울 가능성이 더 큽니다! – idz

답변

1

다형성 지속성은 디자인에 의해 깨진 것 같습니다.

을 위해, 그들은 생성 된 아카이브로 인코딩 유형에 대해 자신의 Radar. 기존 NSCoding의 API (NSKeyedArchiver)과는 달리

, 새로운 스위프트 4 Codable 구현이 쓰는하지 않는 유형의 정보를 가지고 응답을 인용 SR-5331 버그 보고서 유연성과 보안 모두. 따라서 디코드시 API는 값을 디코딩하기 위해 제공 한 구체적인 유형 (예 : 수퍼 클래스 유형) 만 사용할 수 있습니다.

은 의도적으로입니다 -이 작업을 수행하는 데 필요한 동력을 필요로하는 경우, 우리는 당신이 NSKeyedArchiver/NSKeyedUnarchiver을 NSSecureCoding을 채택하고 사용하는 것이 좋습니다 내가 인상적이 모든 빛나는 기사에서 생각 데

그 Codable 기도 중 일부에 대한 해답이었습니다. 객체 팩터 리로 작동하는 코드화 가능 구조체의 병렬 세트는 유형 정보를 보존하기 위해 고려중인 한 가지 해결 방법입니다.

업데이트 다형성 클래스를 재현하는 단일 구조체를 사용하여 샘플을 작성했습니다. 이용 가능 : GitHub.

저는 서브 클래 싱을 통해 쉽게 작동 할 수 있도록 이 아니고입니다. 그러나 기본 프로토콜을 준수하는 클래스는 기본 인코딩에 Codable을 적용 할 수 있습니다. repo에는 키 방식과 키 없음 방식이 모두 포함되어 있습니다. 더 단순한 것은 unkeyed, copied below

// Demo of a polymorphic hierarchy of different classes implementing a protocol 
// and still being Codable 
// This variant uses unkeyed containers so less data is pushed into the encoded form. 
import Foundation 

protocol BaseBeast { 
    func move() -> String 
    func type() -> Int 
    var name: String { get } 
} 

class DumbBeast : BaseBeast, Codable { 
    static let polyType = 0 
    func type() -> Int { return DumbBeast.polyType } 

    var name:String 
    init(name:String) { self.name = name } 
    func move() -> String { return "\(name) Sits there looking stupid" } 
} 

class Flyer : BaseBeast, Codable { 
    static let polyType = 1 
    func type() -> Int { return Flyer.polyType } 

    var name:String 
    let maxAltitude:Int 
    init(name:String, maxAltitude:Int) { 
    self.maxAltitude = maxAltitude 
    self.name = name 
    } 
    func move() -> String { return "\(name) Flies up to \(maxAltitude)"} 
} 


class Walker : BaseBeast, Codable { 
    static let polyType = 2 
    func type() -> Int { return Walker.polyType } 

    var name:String 
    let numLegs: Int 
    let hasTail: Bool 
    init(name:String, legs:Int=4, hasTail:Bool=true) { 
    self.numLegs = legs 
    self.hasTail = hasTail 
    self.name = name 
    } 
    func move() -> String { 
    if numLegs == 0 { 
     return "\(name) Wriggles on its belly" 
    } 
    let maybeWaggle = hasTail ? "wagging its tail" : "" 
    return "\(name) Runs on \(numLegs) legs \(maybeWaggle)" 
    } 
} 

// Uses an explicit index we decode first, to select factory function used to decode polymorphic type 
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type 
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable 
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which 
//: is a valid index into the arrays `encoders` and `factories` 
struct CodableRef : Codable { 
    let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast 

    typealias EncContainer = UnkeyedEncodingContainer 
    typealias DecContainer = UnkeyedDecodingContainer 
    typealias BeastEnc = (inout EncContainer, BaseBeast) throws ->() 
    typealias BeastDec = (inout DecContainer) throws -> BaseBeast 

    static var encoders:[BeastEnc] = [ 
    {(e, b) in try e.encode(b as! DumbBeast)}, 
    {(e, b) in try e.encode(b as! Flyer)}, 
    {(e, b) in try e.encode(b as! Walker)} 
    ] 

    static var factories:[BeastDec] = [ 
    {(d) in try d.decode(DumbBeast.self)}, 
    {(d) in try d.decode(Flyer.self)}, 
    {(d) in try d.decode(Walker.self)} 
    ] 

    init(refTo:BaseBeast) { 
    self.refTo = refTo 
    } 

    init(from decoder: Decoder) throws { 
    var container = try decoder.unkeyedContainer() 
    let typeCode = try container.decode(Int.self) 
    self.refTo = try CodableRef.factories[typeCode](&container) 
    } 

    func encode(to encoder: Encoder) throws { 
    var container = encoder.unkeyedContainer() 
    let typeCode = self.refTo.type() 
    try container.encode(typeCode) 
    try CodableRef.encoders[typeCode](&container, refTo) 
    } 
} 


struct Zoo : Codable { 
    var creatures = [CodableRef]() 
    init(creatures:[BaseBeast]) { 
    self.creatures = creatures.map {CodableRef(refTo:$0)} 
    } 
    func dump() { 
    creatures.forEach { print($0.refTo.move()) } 
    } 
} 


//: ---- Demo of encoding and decoding working ---- 
let startZoo = Zoo(creatures: [ 
    DumbBeast(name:"Rock"), 
    Flyer(name:"Kookaburra", maxAltitude:5000), 
    Walker(name:"Snake", legs:0), 
    Walker(name:"Doggie", legs:4), 
    Walker(name:"Geek", legs:2, hasTail:false) 
    ]) 


startZoo.dump() 
print("---------\ntesting JSON\n") 
let encoder = JSONEncoder() 
encoder.outputFormatting = .prettyPrinted 
let encData = try encoder.encode(startZoo) 
print(String(data:encData, encoding:.utf8)!) 
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData) 

print ("\n------------\nAfter decoding") 

decodedZoo.dump()