如何解决 unarchiveObject(withFile:) 的弃用问题

Posted

技术标签:

【中文标题】如何解决 unarchiveObject(withFile:) 的弃用问题【英文标题】:How to solve deprecation of unarchiveObject(withFile:) 【发布时间】:2019-04-05 10:33:59 【问题描述】:

ios 12.1 中,unarchiveObject(withFile:) 已被弃用。 如何将NSKeyedUnarchiver.unarchiveObject(withFile: String) 转换为使用对NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data: Data)NSKeyedUnarchiver.unarchivedObject(ofClasses: [AnyClass] 的调用,来自:Data) 或NSKeyedUnarchiver.unarchivedObject(ofClass: NSCoding.Protocol, from: Data)

我猜你必须有类似let fileData = try Data(contentsOf: URL) 的东西,然后使用其中一种方法来取消归档数据。但是,我无法弄清楚,并且折旧随附的文档没有帮助(至少对我而言)。

存档的数据相当简单——只是一个字符串数组(此代码定义的类NameToBeSaved的数组):

class NameToBeSaved: NSObject, NSCoding 
var name: String

init(userEnteredName: String) 
    self.name = userEnteredName
    super.init()


func encode(with aCoder: NSCoder) 
    aCoder.encode(name, forKey: "name")


required init?(coder aDecoder: NSCoder) 
    name = aDecoder.decodeObject(forKey: "name") as! String
    super.init()

这里是调用 unarchiveObject(withFile:) 的代码 -

init() 
    if let archivedCategoryNames = NSKeyedUnarchiver.unarchiveObject(withFile: categoryNameArchiveURL.path) as? [NameToBeSaved] 
        allCategories += archivedCategoryNames
     else 
        for category in starterCategories 
            let thisNewCategory = NameToBeSaved(userEnteredName: category)
            createNewCategory(thisNewCategory)
        
        sortCategories()
    

【问题讨论】:

这不是一个答案:但Data You Can Trust WWDC 2018 session 谈论新的 API 以及它们与旧的和现已弃用的 API 的区别。 您问题中的代码是对单个键进行编码或解码。您能否也显示您当前调用unarchiveObject(withFile:) 的代码? @DavidRönnqvist 我已更新帖子以包含调用 unarchiveObject(withFile:) 的代码。我也会看看你提供的链接。感谢您的帮助。 【参考方案1】:

我不知道这是否是最好的解决方案,但这为我解决了转换问题(旧代码注释掉以供比较):

    init() 

    do 
        let rawdata = try Data(contentsOf: categoryNameArchiveURL)
        if let archivedCategoryNames = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(rawdata) as! [NameToBeSaved]? 
            allCategories += archivedCategoryNames
        
     catch 
        print("Couldn't read file")
        for category in starterCategories 
            let thisNewCategory = NameToBeSaved(userEnteredName: category)
            createNewCategory(thisNewCategory)
        
        sortCategories()
    

/*        if let archivedCategoryNames = NSKeyedUnarchiver.unarchiveObject(withFile: categoryNameArchiveURL.path) as? [NameToBeSaved] 
            allCategories += archivedCategoryNames
         else 
            for category in starterCategories 
                let thisNewCategory = NameToBeSaved(userEnteredName: category)
                createNewCategory(thisNewCategory)
            
            sortCategories()
        
 */

【讨论】:

【参考方案2】:

问题:取消归档 sks 文件以用作 SKEmitterNode。

旧方法,已弃用:

let filePath = Bundle.main.path(forResource: "myParticleEmitter", ofType: "sks")!
let burnerPathUnarchived = NSKeyedUnarchiver.unarchiveObject(withFile: burnerPath) as! SKEmitterNode

新方法:

do 
    let fileURL = Bundle.main.url(forResource: "myParticleEmitter", withExtension: "sks")!
    let fileData = try Data(contentsOf: fileURL)
    let unarchivedData = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(burnerData) as! SKEmitterNode
 catch 
    print("didn't work")

那么你可以这样做:

mySKEffectNode.addChild(unarchivedData)
mySKSpriteNode.addChild(mySKEffectNode)

【讨论】:

unarchiveTopLevelObjectWithData 现在也已弃用。使用unarchivedObjectOfClass【参考方案3】:

Swift 5,去掉前缀 'NS' 并集中使用以备将来更改...

class KeyedUnarchiver : NSKeyedUnarchiver 
    open override class func unarchiveObject(with data: Data) -> Any? 
        do 
            let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSObject.self], from: data)
            return object
        
        catch let error 
            Swift.print("unarchiveObject(with:) \(error.localizedDescription)")
            return nil
        
    

    open override class func unarchiveObject(withFile path: String) -> Any? 
        do 
            let data = try Data(contentsOf: URL.init(fileURLWithPath: path))
            let object = try unarchivedObject(ofClasses: [NSObject.self], from: data)
            return object
        
        catch let error 
            Swift.print("unarchiveObject(withFile:) \(error.localizedDescription)")
            return nil
        
    

【讨论】:

【参考方案4】:

作为suggested by Apple,我们应该使用FileManager来读取/写入归档文件。

func archiveURL() -> URL? 
    guard let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first 
        else  return nil 

    return documentURL.appendingPathComponent("MyArchive.data")


func archive(customObject: CustomObject) 
    guard let dataToBeArchived = try? NSKeyedArchiver.archivedData(withRootObject: customObject, requiringSecureCoding: true), 
        let archiveURL = archiveURL() 
        else  
        return
    

    try? dataToBeArchived.write(to: archiveURL)


func unarchive() -> CustomObject? 
    guard let archiveURL = archiveURL(),
        let archivedData = try? Data(contentsOf: archiveURL),
        let customObject = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedData)) as? CustomObject 
        else 
        return nil
    

    return customObject

【讨论】:

以上是关于如何解决 unarchiveObject(withFile:) 的弃用问题的主要内容,如果未能解决你的问题,请参考以下文章

NSKeyedUnarchiver.unarchiveObject() 取消归档旧对象

NSKeyedUnarchiver.unarchiveObject 不工作 [重复]

NSKeyedArchiver.unarchiveObject Swift 3 iOS 10

替换 CloudKit 记录时遇到问题

如何解决“ValueError: Found array with dim 4. Estimator expected <= 2.”?

如何解决此错误 Command PhaseScriptExecution failed with a nonzero exit code