Swift:복잡한 객체를 UserDefaults로 저장

8807 단어 swift
이 디테일을 본 적이 없는데, User Defaults로 좀 복잡하게 저장할 수 있는지의 대상이다.User Defaults의 한 방법setObject: forKey:을 보셨을 거예요. 이 방법으로 저장NSDictionary도 했고 NSArray도 했고 문자열도 저장했어요.
우연히 한 번에 계승자JSONModel의 실체류를 직접 저장한 후에 비극이 되었다.나중에 애플 문서를 찾아봤어요.
The value parameter can be only property list objects: NSData, NSString, 
NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, 
their contents must be property list objects.

간단하게 말하자면 setObject:forKey: 방법은 NSData, NSString 따위의 대상을 저장할 수 있다. NSDictionaryNSArray 메모리에 넣은 요소도property list objects의 것이어야 한다.구체적으로 property list object가 뭐야?JSONModel에 관해서는 여기를 봐도 괜찮아요.
애플의 API가 이 지경에 이르렀으니 다른 생각은 더 이상 할 수 없다.네, 서류를 저장할 수 있어요.근데 여기서 말하는 건 User Defaults잖아.
이 문제를 해결하는 핵심 사상은 하나의 대상을 NSData로 전환하거나 서열화하는 것이다NSData.서열화된 견해가 반드시 정확하지는 않지만 이러한 과정이 존재하고 구체적인 뒤에 자세하게 설명한다.대상이 NSData로 바뀔 수 있다면 NSUserDefaults 방법setObject: forKey:이 적용된다.바로 이런 용법이다.
// 
class UserModel {
    var userId: String = ""
    var accessToken: String = ""
}

// 
let userModel = UserModel()

// 
let userDefaults = NSUserDefaults.standardUserDefaults()
let encodedObject = NSKeyedArchiver.archivedDataWithRootObject(object)
userDefaults.setObject(encodedObject, forKey: "UserInfoKey")
userDefaults.synchronize() // 

대체적인 의미는 위의 코드에서 모두 구현되었다.하지만 위의 코드를 실행하면 틀림없이 오류가 발생할 것이다.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object UserModel for key UserInfoKey'

property list object가 아니기 때문에 실행 방법setObject:forKey을 적용할 때 App이 바로 Crash입니다.
이 질문은property list object에 있는 것 같습니다.그러나 다시 말하자면 우리의 사고방식은 이 사용자 정의 실체류의 대상을NSData으로 바꾸는 것이다.이럴 때는 NSKeyedArchiverNSKeyedUnarchiver를 써야 하고 이것도 간접적으로NSCoding 인터페이스를 사용해야 한다.하나의 실체류가 실현되지 않으면NSCodingNSKeyedArchiverNSKeyedUnarchiver에서 오류가 발생할 수 있기 때문이다.
위 코드에 대한 작은 개선 사항:
class WeiboUserModel: NSObject, NSCoding { //1
    struct PropertyKey {
        static let userIdKey = "userId"
        static let accessTokenKey = "accessToken"
        static let expirationDateKey = "expirationDate"
        static let refreshTokenKey = "refreshToken"
    }

    var userId: String?
    var accessToken: String?
    var expirationDate: NSDate?
    var refreshToken: String?

    func encodeWithCoder(aCoder: NSCoder) {  //2
        aCoder.encodeObject(userId, forKey: PropertyKey.userIdKey)
        aCoder.encodeObject(accessToken, forKey: PropertyKey.accessTokenKey)
        aCoder.encodeObject(expirationDate, forKey: PropertyKey.expirationDateKey)
        aCoder.encodeObject(refreshToken, forKey: PropertyKey.refreshTokenKey)
    }

    required init?(coder aDecoder: NSCoder) { // 3
        userId = aDecoder.decodeObjectForKey(PropertyKey.userIdKey) as? String
        accessToken = aDecoder.decodeObjectForKey(PropertyKey.accessTokenKey) as? String
        expirationDate = aDecoder.decodeObjectForKey(PropertyKey.expirationDateKey) as? NSDate
        refreshToken = aDecoder.decodeObjectForKey(PropertyKey.refreshTokenKey) as? String
    }
}

이렇게 수정하면 그들을 뛰게 할 수 있다.다음은 순서대로 설명한다.실현NSObjectNSCoding.NSObject는 넣지 않아도 되고, @objc로 어떤 방법을 수식해도 된다.NSCoding 인터페이스는 서열화와 반서열화 대상을 제공할 때의 코딩 방법을 제공한다.UserModel의 클래스 이름은 WeiboUserModel로 수정되었습니다.이 부분의 코드는 전체 항목의 일부분이며, 뒤에 보충할 것이다.2. 하나의 대상을 서열화할 때 사용하는 방법func encodeWithCoder(aCoder: NSCoder) 인코딩.3. 역서열화할 때 방법init?(coder aDecoder: NSCoder)으로 디코딩한다.
대체적인 논리가 수정되지 않는 조건하에서 우리는 실체류 대상을 저장할 수 있는 완전한 코드를 보았다.
// 
let userModel = WeiboUserModel()

// 
let userDefaults = NSUserDefaults.standardUserDefaults()
let encodedObject = NSKeyedArchiver.archivedDataWithRootObject(object)
userDefaults.setObject(encodedObject, forKey: "UserInfoKey")
userDefaults.synchronize() // 

이렇게 하면 운행할 수 있다.하지만 우리는 여기서 멈출 수 없다.프로젝트에 저장할 곳이 너무 많을 때, 곳곳에 복사 붙여넣기 NSUserDefaults 실례가 가득 쓰여 있기 때문이다.이런 코드는 너무 경직되어 있다.그리고 마지막 userDefaults.synchronize () 호출을 잊어버리기 쉽다.이로 인해 객체의 스토리지에 문제가 발생할 수 있습니다.
그래서 우리는 이 부분의 코드에 대해 일정한 봉인을 해야 한다.
extension NSUserDefaults { //1
    func saveCustomObject(customObject object: NSCoding, key: String) { //2
        let encodedObject = NSKeyedArchiver.archivedDataWithRootObject(object)
        self.setObject(encodedObject, forKey: key)
        self.synchronize()
    }

    func getCustomObject(forKey key: String) -> AnyObject? { //3
        let decodedObject = self.objectForKey(key) as? NSData

        if let decoded = decodedObject {
            let object = NSKeyedUnarchiver.unarchiveObjectWithData(decoded)
            return object
        }

        return nil
    }
}

우리는 접근 방법을 모두 NSUserDefaults의 확장에 두었다.이렇게 하면 사용자가 사용할 때 NSUserDefaults 자체의 방법과 같을 수 있다.그리고 synchronize() 방법도 안에 봉인되어 있어 더 이상 d대상이 저장되지 않은 것을 잊을 염려가 없습니다.호출된 작은 디테일을 봅시다.
userDefaults.saveCustomObject(customObject: userModel, key: "UserInfoKey") // 

userDefaults.getCustomObject("UserInfoKey") as? WeiboUserModel // 

네, 여기까지.전체 항목의 코드는 여기로 이동하십시오.

to be continued

좋은 웹페이지 즐겨찾기