美文网首页
NSUserDefaults 存储NSDictionary导致c

NSUserDefaults 存储NSDictionary导致c

作者: dequal | 来源:发表于2019-04-21 19:52 被阅读0次

    在iOS开发中,对于轻量级数据的存储, 可以用NSUserDefaults, 例如:

        NSDictionary *dict1 = @{
                               @1:@"A",
                               @2:@"B"
                               };
    
        [[NSUserDefaults standardUserDefaults] setObject:dict1 forKey:@"key1"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    

    这种方式可以很方便地对dict进行本地持久化操作, 接下来我们换一种写法:

        NSDictionary *dict2 = @{
                               @1:@"A",
                               @2:@"B"
                               };
        [[NSUserDefaults standardUserDefaults] setObject:dict2 forKey:@"key1"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    

    可以看到运行后直接crash, 错误信息如下:

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object {
        1 = A;
        2 = B;
    } for key key1'
    

    通过打印dict2 我们可以发现字典的创建方法完全正常, NSDictionary 的 Key 只要是遵循 NSCopying 协议的就行,显然 NSNumber 是遵循的(可自行查看 NSNumber 的定义),因此用 NSNumber 作为 NSDictionary 的 Key 是没问题的, 只是这时候用NSUserDefaults持久化才会遇到这样的问题:

    原因分析:

    通过查看苹果文档:
    https://developer.apple.com/documentation/foundation/nsuserdefaults/1414067-setobject?language=objc,其中有这么一段:

    ## Discussion
    
    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. For more information, see What is a Property List? in Property List Programming Guide.
    Setting a default has no effect on the value returned by the objectForKey: method if the same key exists in a domain that precedes the application domain in the search list.
    
    

    那么什么是Property List呢 ? 继续看官方文档:
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html

    这里总结一下,就是说 虽然 NSDictionary 和 CFDictionary 对象的 Key 可以为任何类型(只要遵循 NSCopying 协议即可),但是如果当 Key 不为字符串 string 对象时,此时这个字典对象就不能算是 property list objects 了,所以它就往 NSUserDefaults 中存储,就会报错.

    解决

    那么对于这种类型的存储, 我们是不是就不能用NSUserDefaults存储呢 ? 答案当然是可以的, 方法有很多, 这里介绍一种,可以在存储前将dict2 转换为所有key都属于string类型再进行存储:

        //这里会将key转为string类型
        NSMutableDictionary *mutaDict = [NSMutableDictionary dictionary];
        [dict2 enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            NSString *keyStr = [[NSString alloc] init];
            keyStr = [NSString stringWithFormat:@"%@",key];
            [mutaDict setObject:obj forKey:keyStr];
        }];
        //此时mutaDict所有的key都属于string类型, 可以直接用NSUserDefaults进行存储
    
    ****注意:

    这里有些文章推荐用NSData进行存储, 也可以达到预期, 但是data和string的转换两次明显成本要高一点.

    拓展

    这里拓展一下, 对于非property list 对象, 除了NSUserDefaults会遇到这种情况, 系统其他任何方法涉有及到 Property List 对象,是不是都需要注意上面的问题呢 ?

    相关文章

      网友评论

          本文标题:NSUserDefaults 存储NSDictionary导致c

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