ios - NSKeyedUnarchiver decodeObjectForKey :]: cannot decode object of class

标签 ios swift xcode throw

我有一个具有多个目标的应用程序(每个目标作为具有不同名称、包标识符等的单独应用程序供另一个客户端使用)。

我有方法:

fileprivate static func loadSessionFromKeychain() -> UserSession? {
    if let sessionData = KeychainWrapper.standard.data(forKey: UserSession.sessionDefaultsKey) {

        print("sessionData:")
        print(sessionData.debugDescription)
            if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession {
                _current = session
                return session
            } else {
                print("ERROR: Could not parse UserSession from Keychain")
            }
        return nil
    }
    return nil
}

if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession { 抛出错误:

* Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '* -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (_test__MyApp.UserSession) for key (root); the class may be defined in source code or a library that is not linked'

我试图捕捉 do {} catch {} 但它没有捕捉到并且仍然抛出相同的错误 + xCode 说

catch' block is unreachable because no errors are thrown in 'do' block`

有什么办法解决这个问题吗?

UserSessionSwift.swift

    import UIKit
    import SwiftKeychainWrapper

    class UserSession: NSObject, NSCoding {
        // Static vars
        fileprivate static var _current: UserSession?
        static var current: UserSession? {
            get {
                // If there is already a session return it
                if _current != nil {
                    return _current
                }
                // If there is no session yet but one is persistently stored return it
                if let session = self.persistentLoadCurrentSession() {
                    _current = session
                    self.persistentLoadCookies()
                    return session
                }
                // Otherwise return nil
                return nil
            }
            set(value) {
                // Store the actual value
                _current = value

                // Perform hooks after changing the current session
                if value == nil {
                    self.afterLogout()
                } else {
                    self.afterLogin()
                }
            }
        }
        // Constants
        fileprivate static let cookiesDefaultsKey: String = "NSUserDefaultsKeyCookieStorage"
        fileprivate static let sessionDefaultsKey: String = "NSUserDefaultsKeyUserSessionStorage"
        // Instance properties
        let client: Client


        // -------------------------------------------------------------------------------
        // MARK: - Lifecycle
        // -------------------------------------------------------------------------------

        required init(client: Client) {
            // set local properties
            self.client = client

            // call super init
            super.init()

            // Store cookies after a session was initialized
            UserSession.persistentStoreCookies()
        }

        required init?(coder aDecoder: NSCoder) {
            self.client = aDecoder.decodeObject(forKey: "client") as! Client
            super.init()
        }


        // -------------------------------------------------------------------------------
        // MARK: - Public
        // -------------------------------------------------------------------------------

        func encode(with aCoder: NSCoder) {
            aCoder.encode(self.client, forKey: "client")
        }

        /**
         Performs all necessary operations after user logs in: stores current cookies and user session for the case user stops and reruns the application later
         */
        static func afterLogin() {
            // Persistently store session data
            self.persistentStoreCookies()
            self.persistentStoreCurrentSession()

            // Register user & device for PUSH notifications
            NotificationsManager.registerForNotifications()
        }

        /**
         Performs all necessary operations after user logs out: deletes stored cookies and user session so that the next time the user runs this application he gets the login prompt
         */
        static func afterLogout() {
            // Erase user session data
            self.persistentEraseCookies()
            self.persistentEraseCurrentSession()

            // Delete all offers from local database
            CoreDataHelper.deleteEntitiesInContext(CoreDataHelper.mainContext, entityName: UsedOffer.entityName)
            CoreDataHelper.saveContext()
        }

        static func requestPopup() {
            // Get popup from server
            print("INFO: Checking for popups on the server...")
            ClientPopupRequest.send({ (popup) -> Void in
                if let popup = popup {
                    // If there is one, show it
                    popup.showAlertAndPerform(in: RootVC.sharedInstance) {
                        // After the popup performs its action, ask for another one
                        self.requestPopup()
                    }
                } else {
                    // If none, exit
                    print("INFO: No new popups found.")
                }
            }) { (error) -> Void in
            }
        }


        // -------------------------------------------------------------------------------
        // MARK: - Private
        // -------------------------------------------------------------------------------

        /**
         Saves current user session to persistent store (currently NSUserDefaults)
         */
        static func persistentStoreCurrentSession() {
            if let session = _current {
                // Archive session
                let sessionData = NSMutableData()
                let archiver = NSKeyedArchiver(forWritingWith: sessionData)
                archiver.encode(session)
                archiver.finishEncoding()
                // Session encoded

                KeychainWrapper.standard.set(session, forKey: UserSession.sessionDefaultsKey)

    //            UserDefaults.standard.set(sessionData, forKey: UserSession.sessionDefaultsKey)
    //            UserDefaults.standard.synchronize()
            } else {
                print("WARNING: No session to store")
            }
        }

        /**
         Tries to load an user session from persistent store (currently NSUserDefaults) and store it as current session in UserSession class. Returns the loaded instance of user session if it succeeds, otherwise returns nil
         */
        fileprivate static func persistentLoadCurrentSession() -> UserSession? {
            if let keychainData = loadSessionFromKeychain() {
                persistentEraseUserDataSession()

                return keychainData
            } else if let userData = loadSessionFromStore() {
                return userData
            }
            return nil
        }

        fileprivate static func loadSessionFromKeychain() -> UserSession? {
            if let sessionData = KeychainWrapper.standard.data(forKey: UserSession.sessionDefaultsKey) {

                print("sessionData:")
                print(sessionData.debugDescription)
                if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession {
                    _current = session
                    return session
                } else {
                    print("ERROR: Could not parse UserSession from Keychain")
                }
                return nil
            }
            return nil
        }

        fileprivate static func loadSessionFromStore() -> UserSession? {
            if let sessionData = UserDefaults.standard.object(forKey: UserSession.sessionDefaultsKey) as? Data {
                let unarchiver = NSKeyedUnarchiver(forReadingWith: sessionData)
                if let session = unarchiver.decodeObject() as? UserSession {
                    unarchiver.finishDecoding()
                    // Session decoded

                    _current = session
                    return session
                } else {
                    print("ERROR: Could not parse UserSession from Store")
                }
                return nil
            }
            return nil
        }

        fileprivate static func persistentEraseCurrentSession() {
            // Remove the current session object
            _current = nil

            // Remove the persisted session object
            UserDefaults.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
            KeychainWrapper.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
        }

        fileprivate static func persistentEraseUserDataSession() {
            // Remove the persisted session object
            UserDefaults.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
        }

        fileprivate static func persistentStoreCookies() {
            if let cookies = HTTPCookieStorage.shared.cookies {
                let cookieData = NSKeyedArchiver.archivedData(withRootObject: cookies)

                UserDefaults.standard.set(cookieData, forKey: UserSession.sessionDefaultsKey)
                KeychainWrapper.standard.set(cookieData, forKey: UserSession.cookiesDefaultsKey)
            } else {
                print("WARNING: No cookies to store")
            }
        }

        fileprivate static func persistentLoadCookies() {

            var cookieData: Data?

            if let keychainData = KeychainWrapper.standard.data(forKey: UserSession.cookiesDefaultsKey) {
                cookieData = keychainData
            } else if let userData = UserDefaults.standard.object(forKey: UserSession.cookiesDefaultsKey) as? Data {
                cookieData = userData
            }

            if (cookieData != nil) {
                if let cookies = NSKeyedUnarchiver.unarchiveObject(with: cookieData!) as? [HTTPCookie] {
                    cookies.forEach { HTTPCookieStorage.shared.setCookie($0) }
                } else {
                    print("ERROR: Could not parse [NSHTTPCookie] from unarchived data")
                }
            } else {
                print("WARNING: No cookies to load")
            }
        }

        fileprivate static func persistentEraseCookies() {
            UserDefaults.standard.removeObject(forKey: UserSession.cookiesDefaultsKey)
            KeychainWrapper.standard.removeObject(forKey: UserSession.cookiesDefaultsKey)
        }
    }

//编辑:添加了 UserSession.swift

最佳答案

你在这里得到的是 exception ;异常无法在 Swift 中捕获或处理,并且不同于 errors ,这就是为什么您不能将调用包装在 do {} catch {} 中的原因。

这里的问题是您的存档包含一个类的名称,该名称在运行时不可用,这可能有多种原因:

  1. 您在包含该类的应用程序中对存档进行了编码,并试图在不包含该类的其他应用程序中进行解码。如果您忘记将类实现与您正在使用的目标链接起来,就会发生这种情况,但在 Swift 中这种情况发生的可能性要小得多,因为您无法导入 header 而忘记链接实现
  2. 类(class)名称已更改。发生这种情况本身可能有几个原因,但在 Swift 中,最可能的原因是您的应用程序/模块名称更改。 Swift 中的类具有运行时名称,其中包括类的完整路径。如果您有一个名为“MyApp”的应用程序,则名为“Foo”的类的限定名称为“MyApp.Foo”。类似地,嵌套在“Foo”中的类“Bar”将具有限定名称“MyApp.Foo.Bar”。重要的是,如果您更改应用的名称(即主模块的名称),类的名称也会更改!

这里可能发生的情况是,您在写入存档后重命名了目标(这会更改类名),或者您在一个目标中写入带有类的存档,但在另一个目标中解码。即使您在两者中包含相同的类,它们的名称也不同(“MyTarget1.UserSession”与“MyTarget2.UserSession”)。

您可以通过几个步骤解决这个问题:

  1. 给类一个稳定的名称,它不会随 @objc 改变,例如@objc(UserSession) class UserSession { ... }。这将为该类提供一个 Objective-C 名称,该名称是常量并且以任何方式依赖于模块名称
  2. 使用NSKeyedUnarchiver.setClass(_:forClassName:)迁移旧文件以使用新的稳定类

参见 NSKeyedArchiver and sharing a custom class between targets有关如何向前迁移存档的完整详细信息。

关于ios - NSKeyedUnarchiver decodeObjectForKey :]: cannot decode object of class,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48666326/

相关文章:

objective-c - 如何在 iOS 中旋转时移动一个 UIView?

iphone - 本地化 iphone app html 文件和奇怪的 xcode 4.2 警告

ios - 如何制作文件夹中的文件列表并通过终端将此列表中的每一行粘贴到 *.plist 中?

ios - 如何使用 Swift 在 iOS 中将字符串转换为 MD5 哈希值?

ios - 使用Autolayout时在layoutSubviews中添加subview是否正确

ios - UILabel textRectForBounds 不工作

ios - 本地原生 iOS 时间黑客证明后台倒数计时器

ios - UILabel 文本编辑后 UITableViewCell 重复

ios - 将 NSFetchedResultController 的结果附加到数组 swift

ios - 使用比例约束会导致不同屏幕尺寸上的故障