为啥 `NSUserDefaults` 不允许存储自定义类的对象?

Posted

技术标签:

【中文标题】为啥 `NSUserDefaults` 不允许存储自定义类的对象?【英文标题】:Why `NSUserDefaults` not allowed to store objects of Custom Classes?为什么 `NSUserDefaults` 不允许存储自定义类的对象? 【发布时间】:2016-06-29 12:13:39 【问题描述】:

我看到NSUserDefaults 不允许除ArrayDictionaryStringNSDataNSNumberBoolNSDate 以外的对象。但是

为什么不允许存储其他对象? 如果我需要将某些属性存储为单个对象怎么办?如果我使用字典来做,我必须在我用来存储值的地方写键。那么,还有什么其他选择。 如果Apple 允许其他对象也存储会出现什么问题。 如果我们使用CoreData 而不是NSUserDefaults 会怎样。我知道NSUserDefaults 全球可用。 如果被终止,即使在我们重新启动应用程序后,在全球范围内提供价值的最佳方式是什么?

正如@Hoa 所建议的,之前我也忘了提到NSCoding 选项

如果我们在自定义类中有很多属性,我们是否需要使用NSCoding 方法对所有属性进行编码和解码?

【问题讨论】:

当然你可以用 NSCoding 方法存储自定义对象。 blog.soff.es/archiving-objective-c-objects-with-nscoding 我认为是最好的前任之一。在这里 - ***.com/questions/2315948/… 【参考方案1】:

您可以将任何对象保存到 plist 文件中,只要它符合 NSCoding 协议即可。

你可以使用类似这样的代码:

+(id) readObjectFromFile:(NSString*) sFileName

    return [NSKeyedUnarchiver unarchiveObjectWithFile:sFileName];



+(bool) saveObject:(id <NSCoding>) anObject ToFile:(NSString*) sFileName

    NSData * data = [NSKeyedArchiver archivedDataWithRootObject:anObject];
    NSError * error;
    [data writeToFile:sFileName options:NSDataWritingAtomic error:&error];
    if (error)
        
            NSLog(@"Save Cats Data error: %@", error.description);
            return NO;
        
    return YES;

Swift 版本:

func readObjectFromFile(sFileName: String) -> AnyObject 
   return NSKeyedUnarchiver.unarchiveObjectWithFile(sFileName)


func saveObject(anObject: AnyObject, ToFile sFileName: String) -> Bool 
    var data: NSData = NSKeyedArchiver.archivedDataWithRootObject(anObject)
    var error: NSError
    data.writeToFile(sFileName, options: NSDataWritingAtomic, error: error)
    if error != nil 
        print("Save Cats Data error: \(error.description)")
        return false
    
    return true

要了解有关 NSCoding 协议的更多信息,您可以阅读: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/

【讨论】:

感谢添加 Swift 版本的人。【参考方案2】:

NSUserDefaults 背后的意图是保存与应用程序状态相关的上下文数据,例如保存用户的首选项、用户停止使用时应用程序的状态(这样您可以在启动时返回到该状态),登录会话数据等。

Core Data 是一种更适合存储持久性数据的方式,您可以根据需要映射数据模型,并且有更广泛的选项来保存数据类型。

虽然 NSUserDefaults 在“任何地方”都可用,但这不应该是决定这是否是保存数据的更好选择的转折点。

您可以编写一个用作数据提供者的单例类,以便您可以像访问 NSUserDefaults 共享实例一样访问您的数据。你只需要记住这个类或模块应该只做一件事,作为你的模型和你的实现之间的接口,所以你不要在这个类中存储任何对象,只需使用它来传递请求以获取并将数据保存到 CoreData。

这个类可能看起来像这样:

class CoreDataProvider 

    static let sharedInstance = SUProvider()

     let managedObjectContext : NSManagedObjectContext
     let sortDescriptor: NSSortDescriptor
     let fetchRequest: NSFetchRequest

     private init()
         managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
         fetchRequest = NSFetchRequest(entityName: "MyObject")
         sortDescriptor = NSSortDescriptor (key:"objectAttribute", ascending:true)

         self.fetchRequest.sortDescriptors = [self.sortDescriptor]
     

     func getSavedObjects() -> [MyObject]? 
        fetchRequest.sortDescriptors = [sortDescriptor]

        do 
            return try self.managedObjectContext.executeFetchRequest(fetchRequest) as? [MyObject]
         catch 
            print("no records found")
        
    

你会这样使用:

func getAllRecords() 
    let records = CoreDataProvider.sharedInstance.getSavedObjects()
    //- Do whatever you need to do

【讨论】:

【参考方案3】:

存储对象的一种临时方法是创建字典或数组的 json 字符串。我在一些低规模的应用程序中使用过它。然后您可以将这些字符串存储在 NSUserDefault.. 中,当您需要使用它时,您可以提取它,并且您可以使用 Object Mapper 库自动将 json 数据映射到对象类型。

例如,您可以在您的类扩展中创建一个函数,在您需要时将 json 数据解析为您的对象。

我建议仅将上述方法用于小型应用程序。如果您要寻找高流量/大型应用程序,您可能想研究 Core Data 甚至 SQlite3..

有任何问题欢迎提问

Reference to Object Mapper library is here

【讨论】:

以上是关于为啥 `NSUserDefaults` 不允许存储自定义类的对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 iOS 中使用 NSUserDefaults?

为啥 NSUserDefaults 在我的应用程序和共享扩展程序之间不起作用?

通过 plist 覆盖 NSUserDefaults 值

为啥存储函数中不允许使用动态 SQL?

为啥我的 NSUserDefaults 无法加载?

为啥 SQL Server 不允许我将 '21/04/17' 存储为日期?