사용자 기본값에 대한 NS코딩으로 사용자 지정 Swift 클래스 저장
현재 NSUserDefaults에 사용자 지정 Swift 클래스를 저장하려고 합니다.내 플레이그라운드의 코드는 다음과 같습니다.
import Foundation
class Blog : NSObject, NSCoding {
var blogName: String?
override init() {}
required init(coder aDecoder: NSCoder) {
if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
self.blogName = blogName
}
}
func encodeWithCoder(aCoder: NSCoder) {
if let blogName = self.blogName {
aCoder.encodeObject(blogName, forKey: "blogName")
}
}
}
var blog = Blog()
blog.blogName = "My Blog"
let ud = NSUserDefaults.standardUserDefaults()
ud.setObject(blog, forKey: "blog")
코드를 실행하면 다음과 같은 오류가 발생합니다.
실행이 중단되었습니다. 이유: 신호 SIGABT.
맨 끝줄에 (ud.setObject
...)
메시지가 있는 앱에서 동일한 코드가 충돌합니다.
"속성 목록의 형식이 잘못되었습니다. 200(속성 목록에는 'CFtype' 유형의 개체를 포함할 수 없습니다.)"
누가 도와줄 수 있습니까?저는 매버릭에서 Xcode 6.0.1을 사용하고 있습니다.감사해요.
Swift 4 이상에서는 Codable을 사용합니다.
당신의 경우 다음 코드를 사용합니다.
class Blog: Codable {
var blogName: String?
}
이제 개체를 만듭니다.예를 들어,
var blog = Blog()
blog.blogName = "My Blog"
이제 다음과 같이 인코딩합니다.
if let encoded = try? JSONEncoder().encode(blog) {
UserDefaults.standard.set(encoded, forKey: "blog")
}
다음과 같이 해독합니다.
if let blogData = UserDefaults.standard.data(forKey: "blog"),
let blog = try? JSONDecoder().decode(Blog.self, from: blogData) {
}
첫 번째 문제는 다음과 같이 구분되지 않은 클래스 이름을 가져야 한다는 것입니다.
@objc(Blog)
class Blog : NSObject, NSCoding {
그런 다음 개체를 사용자 기본값에 저장하기 전에 개체를 NSData로 인코딩해야 합니다.
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
마찬가지로 개체를 복원하려면 개체 보관을 해제해야 합니다.
if let data = ud.objectForKey("blog") as? NSData {
let unarc = NSKeyedUnarchiver(forReadingWithData: data)
unarc.setClass(Blog.self, forClassName: "Blog")
let blog = unarc.decodeObjectForKey("root")
}
운동장에서 그것을 사용하지 않을 경우, 손으로 수업을 등록할 필요가 없기 때문에 조금 더 간단합니다.
if let data = ud.objectForKey("blog") as? NSData {
let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}
@dan-beaulieu가 제안한 것처럼, 나는 나 자신의 질문에 답합니다.
작업 코드는 다음과 같습니다.
참고: Playgrounds에서 코드가 작동하는 데 클래스 이름을 디매그링할 필요가 없습니다.
import Foundation
class Blog : NSObject, NSCoding {
var blogName: String?
override init() {}
required init(coder aDecoder: NSCoder) {
if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
self.blogName = blogName
}
}
func encodeWithCoder(aCoder: NSCoder) {
if let blogName = self.blogName {
aCoder.encodeObject(blogName, forKey: "blogName")
}
}
}
let ud = NSUserDefaults.standardUserDefaults()
var blog = Blog()
blog.blogName = "My Blog"
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
if let data = ud.objectForKey("blog") as? NSData {
let unarc = NSKeyedUnarchiver(forReadingWithData: data)
let newBlog = unarc.decodeObjectForKey("root") as Blog
}
여기 Swift 4 & 5의 완벽한 솔루션이 있습니다.
첫째, 헬퍼 메소드를 실행합니다.UserDefaults
확장명:
extension UserDefaults {
func set<T: Encodable>(encodable: T, forKey key: String) {
if let data = try? JSONEncoder().encode(encodable) {
set(data, forKey: key)
}
}
func value<T: Decodable>(_ type: T.Type, forKey key: String) -> T? {
if let data = object(forKey: key) as? Data,
let value = try? JSONDecoder().decode(type, from: data) {
return value
}
return nil
}
}
예를 들어, 사용자 지정 개체를 저장하고 로드하려고 합니다.Dummy
두 개의 기본 필드가 있습니다.Dummy
준수해야 함Codable
:
struct Dummy: Codable {
let value1 = "V1"
let value2 = "V2"
}
// Save
UserDefaults.standard.set(encodable: Dummy(), forKey: "K1")
// Load
let dummy = UserDefaults.standard.value(Dummy.self, forKey: "K1")
Swift 2.1 및 Xcode 7.1.1로 테스트됨
blogName이 선택 사항이 될 필요가 없다면 약간 다른 구현을 권장합니다.
class Blog : NSObject, NSCoding {
var blogName: String
// designated initializer
//
// ensures you'll never create a Blog object without giving it a name
// unless you would need that for some reason?
//
// also : I would not override the init method of NSObject
init(blogName: String) {
self.blogName = blogName
super.init() // call NSObject's init method
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(blogName, forKey: "blogName")
}
required convenience init?(coder aDecoder: NSCoder) {
// decoding could fail, for example when no Blog was saved before calling decode
guard let unarchivedBlogName = aDecoder.decodeObjectForKey("blogName") as? String
else {
// option 1 : return an default Blog
self.init(blogName: "unnamed")
return
// option 2 : return nil, and handle the error at higher level
}
// convenience init must call the designated init
self.init(blogName: unarchivedBlogName)
}
}
테스트 코드는 다음과 같습니다.
let blog = Blog(blogName: "My Blog")
// save
let ud = NSUserDefaults.standardUserDefaults()
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
ud.synchronize()
// restore
guard let decodedNSData = ud.objectForKey("blog") as? NSData,
let someBlog = NSKeyedUnarchiver.unarchiveObjectWithData(decodedNSData) as? Blog
else {
print("Failed")
return
}
print("loaded blog with name : \(someBlog.blogName)")
마지막으로 NSUserDefaults를 사용하는 대신 NSKeyedArchiver를 사용하고 사용자 지정 개체 배열을 파일에 직접 저장하는 것이 더 쉽다는 점을 지적하고자 합니다.그들의 차이점에 대해서는 여기 제 답변에서 자세히 확인하실 수 있습니다.
Swift 4에는 NS 코딩 프로토콜을 대체하는 새로운 프로토콜이 있습니다.라고 합니다.Codable
클래스와 스위프트 타입을 지원합니다! (이음, 구조):
struct CustomStruct: Codable {
let name: String
let isActive: Bool
}
스위프트 3 버전:
class CustomClass: NSObject, NSCoding {
let name: String
let isActive: Bool
init(name: String, isActive: Bool) {
self.name = name
self.isActive = isActive
}
// MARK: NSCoding
required convenience init?(coder decoder: NSCoder) {
guard let name = decoder.decodeObject(forKey: "name") as? String,
let isActive = decoder.decodeObject(forKey: "isActive") as? Bool
else { return nil }
self.init(name: name, isActive: isActive)
}
func encode(with coder: NSCoder) {
coder.encode(self.name, forKey: "name")
coder.encode(self.isActive, forKey: "isActive")
}
}
저는 제 구조를 사용합니다.훨씬 쉬워요.
struct UserDefaults {
private static let kUserInfo = "kUserInformation"
var UserInformation: DataUserInformation? {
get {
guard let user = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaults.kUserInfo) as? DataUserInformation else {
return nil
}
return user
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: UserDefaults.kUserInfo)
}
}
}
사용 방법:
let userinfo = UserDefaults.UserInformation
UserDefaults에 Custom Objects를 저장하는 간단한 예는 다음과 같습니다.
GREAT 'CODABLE'의 도움으로 개체를 저장/검색하는 보일러 플레이트 코드를 더 이상 작성할 필요가 없습니다. 이것이 짜증나는 수동 인코딩/디코딩으로부터 보호할 수 있는 것입니다.
따라서 이미 NS Coding을 사용하고 있는 경우 코드에서 아래의 두 가지 방법을 제거하고 Codable(암호화 + 복호화의 결합) 프로토콜로 전환합니다.
required init(coder decoder: NSCoder) // NOT REQUIRED ANY MORE, DECODABLE TAKES CARE OF IT
func encode(with coder: NSCoder) // NOT REQUIRED ANY MORE, ENCODABLE TAKES CARE OF IT
이제 Codable의 단순성부터 시작해 보겠습니다.
UserDefaults에 저장할 사용자 정의 클래스 또는 구조물 만들기
struct Person : Codable {
var name:String
}
OR
class Person : Codable {
var name:String
init(name:String) {
self.name = name
}
}
개체를 아래와 같이 UserDefaults에 저장합니다.
if let encoded = try? JSONEncoder().encode(Person(name: "Dhaval")) {
UserDefaults.standard.set(encoded, forKey: "kSavedPerson")
}
아래와 같이 UserDefaults에서 개체 로드
guard let savedPersonData = UserDefaults.standard.object(forKey: "kSavedPerson") as? Data else { return }
guard let savedPerson = try? JSONDecoder().decode(Person.self, from: savedPersonData) else { return }
print("\n Saved person name : \(savedPerson.name)")
바로 그겁니다.
이 질문이 오래된 질문이라는 것을 알고 있지만 도움이 될 만한 작은 도서관을 썼습니다.
개체를 다음과 같은 방식으로 저장합니다.
class MyObject: NSObject, Codable {
var name:String!
var lastName:String!
enum CodingKeys: String, CodingKey {
case name
case lastName = "last_name"
}
}
self.myStoredPref = Pref<MyObject>(prefs:UserDefaults.standard,key:"MyObjectKey")
let myObject = MyObject()
//... set the object values
self.myStoredPref.set(myObject)
그런 다음 개체를 원래 값으로 다시 추출합니다.
let myStoredValue: MyObject = self.myStoredPref.get()
클래스에 Codable 프로토콜을 구현한 후 PropertyListEncoder를 사용하여 사용자 지정 개체를 UserDefaults에 저장할 수 있습니다.사용자 지정 클래스를 사용자로 설정
class User: NSObject, Codable {
var id: Int?
var name: String?
var address: String?
}
Codable은 Decodable과 Encodable 프로토콜을 모두 구현한다는 것을 의미합니다.이름에서 알 수 있듯이, Encodable 및 Decodable을 구현함으로써 JSON 또는 Property List와 같은 외부 표현으로 인코딩 및 디코딩할 수 있는 클래스 기능을 제공합니다.
이제 모델을 속성 목록으로 변환하여 저장할 수 있습니다.
if let encodedData = try? PropertyListEncoder().encode(userModel){
UserDefaults.standard.set(encodedData, forKey:"YOUR_KEY")
UserDefaults.standard.synchronize();
}
그리고 PropertyListDecoder를 사용하여 모델을 검색할 수 있습니다.당신이 그것을 속성 목록으로 인코딩했기 때문입니다.
if let data = UserDefaults.standard.value(forKey:"YOUR_KEY") as? Data {
return try? PropertyListDecoder().decode(User.self, from:data)
}
속성 목록에 개체를 직접 저장할 수 없으며 개별 Strings 또는 기타 기본 유형(정수 등)만 저장할 수 있습니다.따라서 다음과 같은 개별 문자열로 저장해야 합니다.
override init() {
}
required public init(coder decoder: NSCoder) {
func decode(obj:AnyObject) -> AnyObject? {
return decoder.decodeObjectForKey(String(obj))
}
self.login = decode(login) as! String
self.password = decode(password) as! String
self.firstname = decode(firstname) as! String
self.surname = decode(surname) as! String
self.icon = decode(icon) as! UIImage
}
public func encodeWithCoder(coder: NSCoder) {
func encode(obj:AnyObject) {
coder.encodeObject(obj, forKey:String(obj))
}
encode(login)
encode(password)
encode(firstname)
encode(surname)
encode(icon)
}
언급URL : https://stackoverflow.com/questions/26469457/saving-custom-swift-class-with-nscoding-to-userdefaults
'bestsource' 카테고리의 다른 글
소켓 옵션이 리스닝 소켓에서 수락()을 통해 상속됩니까? (0) | 2023.11.05 |
---|---|
VBA 코드 벤치마킹 (0) | 2023.11.05 |
MySQL 워크벤치와 MAMP PRO를 연결하는 방법은? (0) | 2023.11.05 |
mariadb REGEXP_INSTR은 괄호를 사용할 때 0 또는 1을 반환합니다. (0) | 2023.11.05 |
argv 포인터를 글로벌하게 사용해도 안전합니까? (0) | 2023.11.05 |