Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 尝试将非属性列表对象设置为NSUserDefaults_Ios_Objective C_Encoding_Nsuserdefaults - Fatal编程技术网

Ios 尝试将非属性列表对象设置为NSUserDefaults

Ios 尝试将非属性列表对象设置为NSUserDefaults,ios,objective-c,encoding,nsuserdefaults,Ios,Objective C,Encoding,Nsuserdefaults,我以为我知道是什么导致了这个错误,但我似乎无法找出我做错了什么 以下是我收到的完整错误消息: Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray 在应用程序中的某个点上,设置了BC_PersonClass中的NSString,我有一个DataSave类,我认为该类正在处理对我的BC_Per

我以为我知道是什么导致了这个错误,但我似乎无法找出我做错了什么

以下是我收到的完整错误消息:

Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray 在应用程序中的某个点上,设置了
BC_PersonClass
中的
NSString
,我有一个
DataSave
类,我认为该类正在处理对我的
BC_PersonClass
中的属性进行编码。 下面是我在
DataSave
类中使用的代码:

- (void)savePersonArrayData:(BC_Person *)personObject
{
   // NSLog(@"name of the person %@", personObject.personsName);

    [mutableDataArray addObject:personObject];

    // set the temp array to the mutableData array
    tempMuteArray = [NSMutableArray arrayWithArray:mutableDataArray];

    // save the person object as nsData
    NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];

    // first add the person object to the mutable array
    [tempMuteArray addObject:personEncodedObject];

    // NSLog(@"Objects in the array %lu", (unsigned long)mutableDataArray.count);

    // now we set that data array to the mutable array for saving
    dataArray = [[NSArray alloc] initWithArray:mutableDataArray];
    //dataArray = [NSArray arrayWithArray:mutableDataArray];

    // save the object to NS User Defaults
    NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
    [userData setObject:dataArray forKey:@"personDataArray"];
    [userData synchronize];
}
我希望这是足够的代码给你一个想法,我想做什么。 再一次,我知道我的问题在于如何在我的BC_Person类中编码我的属性,我似乎不知道我做错了什么


谢谢你的帮助

您发布的代码试图将自定义对象数组保存到
NSUserDefaults
。你不能那样做。实现
NSCoding
方法没有帮助。您只能在
nsserdefaults
中存储
NSArray
NSDictionary
NSData
NSNumber
NSDate
等内容

您需要将对象转换为
NSData
(就像您在一些代码中所做的那样),并将该
NSData
存储在
NSUserDefaults
中。如果需要,您甚至可以存储
NSData
NSArray

当您读回数组时,需要取消归档
NSData
,以取回
BC\u Person
对象

也许你想要这个:

- (void)savePersonArrayData:(BC_Person *)personObject {
    [mutableDataArray addObject:personObject];

    NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
    for (BC_Person *personObject in mutableDataArray) { 
        NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
        [archiveArray addObject:personEncodedObject];
    }

    NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
    [userData setObject:archiveArray forKey:@"personDataArray"];
}

在我看来,自己运行数组并将对象编码到NSData中是相当浪费的。您的错误
BC\u Person是一个非属性列表对象
告诉您框架不知道如何序列化Person对象

因此,需要做的只是确保person对象符合NSCoding,然后您可以简单地将自定义对象数组转换为NSData并将其存储为默认值。这里有一个游乐场:

编辑:写入
NSUserDefaults
在Xcode 7上被破坏,因此游乐场将存档到数据并返回并打印输出。UserDefaults步骤包括在内,以防其在以后的某一点被修复

//: Playground - noun: a place where people can play

import Foundation

class Person: NSObject, NSCoding {
    let surname: String
    let firstname: String

    required init(firstname:String, surname:String) {
        self.firstname = firstname
        self.surname = surname
        super.init()
    }

    //MARK: - NSCoding -
    required init(coder aDecoder: NSCoder) {
        surname = aDecoder.decodeObjectForKey("surname") as! String
        firstname = aDecoder.decodeObjectForKey("firstname") as! String
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(firstname, forKey: "firstname")
        aCoder.encodeObject(surname, forKey: "surname")
    }
}

//: ### Now lets define a function to convert our array to NSData

func archivePeople(people:[Person]) -> NSData {
    let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
    return archivedObject
}

//: ### Create some people

let people = [Person(firstname: "johnny", surname:"appleseed"),Person(firstname: "peter", surname: "mill")]

//: ### Archive our people to NSData

let peopleData = archivePeople(people)

if let unarchivedPeople = NSKeyedUnarchiver.unarchiveObjectWithData(peopleData) as? [Person] {
    for person in unarchivedPeople {
        print("\(person.firstname), you have been unarchived")
    }
} else {
    print("Failed to unarchive people")
}

//: ### Lets try use NSUserDefaults
let UserDefaultsPeopleKey = "peoplekey"
func savePeople(people:[Person]) {
    let archivedObject = archivePeople(people)
    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setObject(archivedObject, forKey: UserDefaultsPeopleKey)
    defaults.synchronize()
}

func retrievePeople() -> [Person]? {
    if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsPeopleKey) as? NSData {
        return NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person]
    }
    return nil
}

if let retrievedPeople = retrievePeople() {
    for person in retrievedPeople {
        print("\(person.firstname), you have been unarchived")
    }
} else {
    print("Writing to UserDefaults is still broken in playgrounds")
}

瞧,您已经将一组自定义对象存储到了NSUserDefaults中。首先,rmaddy的答案(上面)是正确的:实现
NSCoding
没有帮助。然而,您需要实现
NSCoding
来使用
NSKeyedArchiver
以及所有这些,所以这只是又一个步骤。。。通过
NSData
转换

示例方法

- (NSUserDefaults *) defaults {
    return [NSUserDefaults standardUserDefaults];
}

- (void) persistObj:(id)value forKey:(NSString *)key {
    [self.defaults setObject:value  forKey:key];
    [self.defaults synchronize];
}

- (void) persistObjAsData:(id)encodableObject forKey:(NSString *)key {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encodableObject];
    [self persistObj:data forKey:key];
}    

- (id) objectFromDataWithKey:(NSString*)key {
    NSData *data = [self.defaults objectForKey:key];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
因此,您可以将
NSCoding
对象包装在
NSArray
NSDictionary
或任何东西中

要保存:

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];
要获得:

NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
删除

[currentDefaults removeObjectForKey:@"yourKeyName"];

我在尝试将字典保存到
NSUserDefaults
时遇到问题。结果证明它不会保存,因为它包含
NSNull
值。所以我只是将字典复制到一个可变字典中,删除了空值,然后保存到
NSUserDefaults

NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];
在这种情况下,我知道哪些键可能是
NSNull
值。

Swift 3解决方案 简单实用程序类 模范班 如何打电话

默认对象必须是属性列表,即NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary的实例(或集合的实例组合)

如果要存储任何其他类型的对象,通常应将其存档以创建NSData实例。有关详细信息,请参阅《首选项和设置编程指南》

Swift-4 Xcode 9.1 尝试此代码

您不能在NSUserDefault中存储映射器,您只能存储NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary。

let myData = NSKeyedArchiver.archivedData(withRootObject: myJson)
UserDefaults.standard.set(myData, forKey: "userJson")

let recovedUserJsonData = UserDefaults.standard.object(forKey: "userJson")
let recovedUserJson = NSKeyedUnarchiver.unarchiveObject(with: recovedUserJsonData)
Swift 5非常简单的方法

//MARK:- First you need to encoded your arr or what ever object you want to save in UserDefaults
//in my case i want to save Picture (NMutableArray) in the User Defaults in
//in this array some objects are UIImage & Strings

//first i have to encoded the NMutableArray 
let encodedData = NSKeyedArchiver.archivedData(withRootObject: yourArrayName)
//MARK:- Array save in UserDefaults
defaults.set(encodedData, forKey: "YourKeyName")

//MARK:- When you want to retreive data from UserDefaults
let decoded  = defaults.object(forKey: "YourKeyName") as! Data
yourArrayName = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! NSMutableArray

//MARK: Enjoy this arrry "yourArrayName"
Swift 5:可以使用可编码的协议代替NSKeyedArchiever

Pref结构是围绕UserDefaults标准对象的自定义包装

struct Pref {
    static let keyUser = "Pref.User"
    static var user: User? {
        get {
            if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
                do {
                    return try JSONDecoder().decode(User.self, from: data)
                } catch {
                    print("Error while decoding user data")
                }
            }
            return nil
        }
        set {
            if let newValue = newValue {
                do {
                    let data = try JSONEncoder().encode(newValue)
                    UserDefaults.standard.set(data, forKey: keyUser)
                } catch {
                    print("Error while encoding user data")
                }
            } else {
                UserDefaults.standard.removeObject(forKey: keyUser)
            }
        }
    }
}
因此,您可以这样使用它:

Pref.user?.name = "John"

if let user = Pref.user {...

Swift
@propertyWrapper

Codable
对象保存到
UserDefault

@propertyWrapper
    struct UserDefault<T: Codable> {
        let key: String
        let defaultValue: T

        init(_ key: String, defaultValue: T) {
            self.key = key
            self.defaultValue = defaultValue
        }

        var wrappedValue: T {
            get {

                if let data = UserDefaults.standard.object(forKey: key) as? Data,
                    let user = try? JSONDecoder().decode(T.self, from: data) {
                    return user

                }

                return  defaultValue
            }
            set {
                if let encoded = try? JSONEncoder().encode(newValue) {
                    UserDefaults.standard.set(encoded, forKey: key)
                }
            }
        }
    }




enum GlobalSettings {

    @UserDefault("user", defaultValue: User(name:"",pass:"")) static var user: User
}
如何使用它

//Set value 
 GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")

//GetValue
print(GlobalSettings.user)

我遇到了这个问题,最终发现这是因为我试图使用
NSNumber
作为字典键,而属性列表只允许字符串作为键。的文档没有提到此限制,但其链接的页面有:

按惯例,表2-1中列出的每个可可和核心基础对象被称为属性列表对象。从概念上讲,您可以将“属性列表”视为所有这些类的抽象超类。如果您从某个方法或函数接收属性列表对象,您知道它必须是这些类型之一的实例,但您可能事先不知道是哪种类型。如果属性列表对象是容器(即数组或字典),则其中包含的所有对象也必须是属性列表对象。如果数组或字典包含的对象不是属性列表对象,则无法使用各种属性列表方法和函数保存和恢复数据层次结构。尽管NSDictionary和CFDictionary对象允许其键为任何类型的对象,但如果键不是字符串对象,则集合不是属性列表对象


(强调我的)

我有一个问题。如果我想将personEncodedObject添加到数组中,然后将数组放入用户数据中。。。我是否可以替换:[archiveArray addObject:personEncodedObject];使用NSArray并将addObject:personEncodedObject添加到该数组中,然后将其保存在userData中?如果你听我的话,嗯?我认为你输入错了,因为你想用同一行代码替换一行代码。我的代码确实将编码对象数组放入了用户默认值中。我想我迷路了,因为我以为你只能使用
struct User: Codable {
    let id: String
    let mail: String
    let fullName: String
}
struct Pref {
    static let keyUser = "Pref.User"
    static var user: User? {
        get {
            if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
                do {
                    return try JSONDecoder().decode(User.self, from: data)
                } catch {
                    print("Error while decoding user data")
                }
            }
            return nil
        }
        set {
            if let newValue = newValue {
                do {
                    let data = try JSONEncoder().encode(newValue)
                    UserDefaults.standard.set(data, forKey: keyUser)
                } catch {
                    print("Error while encoding user data")
                }
            } else {
                UserDefaults.standard.removeObject(forKey: keyUser)
            }
        }
    }
}
Pref.user?.name = "John"

if let user = Pref.user {...
@propertyWrapper
    struct UserDefault<T: Codable> {
        let key: String
        let defaultValue: T

        init(_ key: String, defaultValue: T) {
            self.key = key
            self.defaultValue = defaultValue
        }

        var wrappedValue: T {
            get {

                if let data = UserDefaults.standard.object(forKey: key) as? Data,
                    let user = try? JSONDecoder().decode(T.self, from: data) {
                    return user

                }

                return  defaultValue
            }
            set {
                if let encoded = try? JSONEncoder().encode(newValue) {
                    UserDefaults.standard.set(encoded, forKey: key)
                }
            }
        }
    }




enum GlobalSettings {

    @UserDefault("user", defaultValue: User(name:"",pass:"")) static var user: User
}
struct User:Codable {
    let name:String
    let pass:String
}
//Set value 
 GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")

//GetValue
print(GlobalSettings.user)