美文网首页Foundatation
603,NSUserDefault的原理(原理:NSUserDe

603,NSUserDefault的原理(原理:NSUserDe

作者: 枫叶1234 | 来源:发表于2021-04-01 10:27 被阅读0次
    NSUserDefaults是什么,有什么用处

    原理:NSUserDefaults类提供了与默认数据库相交互的编程接口。其实它存储在应用程序的一个plist文件里,路径为沙盒Document目录平级的/Library/Prefereces里。如果将默认数据库比喻为SQL数据库,那么NSUserDefaults就相当于SQL语句。

    对于应用来说,每个用户都有自己的独特偏好设置,而好的应用会让用户根据喜好选择合适的使用方式,把这些偏好记录在应用包的plist文件中,通过NSUserDefaults类来访问,这是NSUserDefaults的常用姿势。如果有一些设置你希望用户即使升级后还可以继续使用,比如玩游戏时得过的最高分、喜好和通知设置、主题颜色甚至一个用户头像,那么你可以使用NSUserDefaults来存储这些信息,如果有更多需求,可以了解数据持久化相关的知识。

    具体来说NSUserDefaults是iOS系统提供的一个单例类(iOS提供了若干个单例类),通过类方法standardUserDefaults可以获取NSUserDefaults单例。如:

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    

    NSUserDefaults 单例以 key-value 的形式存储了一系列偏好设置, key 是名称, value 是相应的数据。存/取数据时可以使用方法 objectForKey: 和 setObject:forKey: 来把对象存储到相应的 plist 文件中,或者读取,既然是 plist 文件,那么对象的类型则必须是 plist 文件可以存储的类型,正如官方文档中提到的——

    • NSData
    • NSString
    • NSNumber
    • NSDate
    • NSArray
    • NSDictionary

    而如果需要存储plist文件不支持的类型,比如图片,可以先将其归档为NSData类型,再存入plist文件,需要注意的是,即使对象是NSArray或NSDictionary,他们存储的类型也应该是以上范围包括的。

    存/读不同类型数据

    比如存/读一个整数、字符串和一张图片:

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:@”jack“ forKey:@"firstName"];
    [defaults setInteger:10 forKey:@"Age"];
     
    UIImage *image =[UIImage imageNamed:@"somename"];
    NSData *imageData = UIImageJPEGRepresentation(image, 100);//把image归档为NSData
    [defaults setObject:imageData forKey:@"image"];
     
    [defaults synchronize];
    

    其中,方法 synchronise 是为了强制存储,其实并非必要,因为这个方法会在系统中默认调用,但是你确认需要马上就存储,这样做是可行的。

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *firstName = [defaults objectForKey:@"firstName"]
    NSInteger age = [defaults integerForKey:@"Age"];
     
    NSData *imageData = [defaults dataForKey:@"image"];
    UIImage *image = [UIImage imageWithData:imageData];
    

    我们通过为三个数据设置 key 的方式把 NSInteger 、 NSString 和 UIImage 三种数据存储下来,其中图片是通过归档为 NSData 的方式进行存储的,除此之外,还可以被转为 NSNumber 或 NSString 类型。顺便提一句,这里 NSInteger 没有星号,因为 NSInteger 根据系统是64位还是32位来判断自身是 long 还是 int 类型,并且它也不是一个标准Objective-C对象。

    简便方法存取不同类型数据

    由上边的例子可以看到一个方法-setInteger:,这跟常用的-setObject:相比设置类型更明确。其实,NSUserDefaults提供了若干简便方法可以存储某些常用类型的值,例如:

    - setBool:forKey:
    - setFloat:forKey:
    - setInteger:forKey:
    - setDouble:forKey:
    - setURL:forKey:
    

    这将使某些值的设置更简单。

    NSUserDefaults域

    考虑这么一种情况:

    BOOL showTutorialOnLaunch = [[NSUserDefaults standardUserDefaults] boolForKey:@"ShowTutorial"];
    

    这种情况下,当key值 @“ShowTutorial” 已设置后会运行正确。但如果默认数据库没有这个key的默认值时,将会返回 NO ,这或许就不一定是你需要的值了,因为无法区分 NO 和 no value ,前一段所提到的简便方法大多有这种问题。
    解决方式:使用registerDefaults:方法
    首先创建一个包含用户偏好设置信息的DefaultPreferences.plist文件,添加到target中。在运行时,app就可以加载这个文件并且把内容传到registerDefaults :

    
    NSURL *defaultPrefsFile = [[NSBundle mainBundle]
    URLForResource:@"DefaultPreferences" withExtension:@"plist"];
    NSDictionary *defaultPrefs = [NSDictionary dictionaryWithContentsOfURL:defaultPrefsFile];
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs];
    

    注意需要在每次启动app并且没有在user defaules中读取数据的时候调用以上方法,因为 registerDefaults: 不能把这些默认数据存储到硬盘上,所以 application:didFinishLaunchingWithOptions 是最合适的地方。
    这样做的原因是:默认情况下,应用域是空的,没见键也没有值。当应用第一次设置某项用户偏好设置的值时,相应的值会通过指定的键加入应用域。当通过NSUserDefaults获取某项用户偏好设置的值时,NSUserDefaults会先在应用域中查找,如果找到了值,NSUserDefaults就会返回这个值。如果没有找到,NSUserDefaults就会在注册域中查找并返回默认值。

    user defaults数据库中其实是由多个层级的域组成的,当你读取一个键值的数据时,NSUserDefaults从上到下透过域的层级寻找正确的值,不同的域有不同的功能,有些域是可持久的,有些域则不行。

    • 应用域(application domain)是最重要的域,它存储着你app通过NSUserDefaults set...forKey添加的设置。
    • 注册域(registration domain)仅有较低的优先权,只有在应用域没有找到值时才从注册域去寻找。
    • 全局域(global domain)则存储着系统的设置
    • 语言域(language-specific domains)则包括地区、日期等
    • 参数域( argument domain)有最高优先权

    简单来说, 我们调用的类似这样的方法:UserDefaults.standard.set(true, forKey: "isLogin”)
    都是在Application这个域上面存储的,但 UserDefaults还包括了其他4个域,那么为什么要有域这样的设计呢。这可以从register方法说起。register方法我们刚刚看到了,可以为指定的key注册默认值。但我们深入思考一下,这个默认值又是怎么存储和实现的呢?
    其实register方法所做的事情非常简单,只是将我们传递给它的参数都设置到了NSRegistrationDomain 这个域中。 然后我们每次调用 UserDefaults.standard.bool(forKey:"isLogin")这样的读取数据的方法时,实际上会在底层的存储结构中进行一次搜索,属性搜索过程就是这样:NSArgumentDomain -> Application -> NSGlobalDomain -> Languages -> NSRegistrationDomain

    举个例子:
    UserDefaults.standard.register(defaults: ["FloatValue":3.0]) UserDefaults.standard.float(forKey:"FloatValue")
    比如我们例子中的FloatValue,由于我们之前使用register将它设置到了NSRegistrationDomain 域中,并且由于我们没有调用set(_value:Float, forKey defaultName:String)方法将它设置到Application域中。 所以按照 UserDefaults的默认搜索顺序,就会找到最后NSRegistrationDomain 域中的那个FloatValue,也就是我们所谓的默认值 3.0 了。相反,如果设置了值,那么就会从Application中获取值,就是我们日常使用情景。

    相关文章

      网友评论

        本文标题:603,NSUserDefault的原理(原理:NSUserDe

        本文链接:https://www.haomeiwen.com/subject/gbzzhltx.html