先来看下官方文档对二者的注释:
setValue:forKey:
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
官方解释大致意为: 调用setObject:forKey方法,但如果value为nil,则调用removeObjectForKey:方法
PS: 可以看到setValue:forKey:方法的key是NSString类型
setObject:forKey:
- (void)setObject:(ObjectType)anObject
forKey:(id<NSCopying>)aKey;
setObject:forKey:方法的key的类型是id类型
二者相同点:
- key都不能为nil,否则发生崩溃(报错: -[__NSDictionaryM setObject:forKey:]: key cannot be nil)
不同点:
- setValue:forKey:的key是字符串类型,而setObject:forKey:的key是id类型
- setValue:forKey:的value可以为nil,value为nil时会自动调用removeObjectForKey:; setObject:forKey:的value不能为nil,否则发生崩溃(-[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: ))
总结来说:
setObject:forKey:中的key和value可以是除了nil以外的任意对象,包括[NSNull null]空对象
setValue:forKey:的key是字符串类型,value可以是nil,可以是[NSNull null]空对象及其他任意对象
NSMutableDictionary *mDic = [NSMutableDictionary dictionary];
[mDic setObject:@"value1" forKey:@"key1"];
[mDic setValue:@"value2" forKey:@"key2"];
mDic[@"key3"] = nil;
针对mDic的上面的三种赋值方式:
我们知道setObject:forKey:方式的value不能为nil,而mDic[@"key"] = nil; 不会发生崩溃,可猜测: mDic[@"key"] = nil; 写法等同于调用setValue:forKey:方式赋值
PS: 以上结论均针对调用者为NSMutableDictionary而言
对于NSUserDefaults而言
在NSUserDefaults.h中我们可以看到如下两个方法:
/*!
-setObject:forKey: immediately stores a value (or removes the value if nil is passed as the value) for the provided key in the search list entry for the receiver's suite name in the current user and any host, then asynchronously stores the value persistently, where it is made available to other processes.
*/
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
/// -removeObjectForKey: is equivalent to -[... setObject:nil forKey:defaultName]
- (void)removeObjectForKey:(NSString *)defaultName;
NSUserDefaults是一个单例,在整个程序中只存在一个NSUserDefaults实例对象,用于数据的持久化保存。
我们知道NSUserDefaults实际是plist方式存储,我们可以查看程序默认的info.plist查看其支持的类型: NSArray、NSDictionary、BOOL、NSData、NSDate、NSNumber、NSString。

对于NSUserDefaults的实例调用setObject:forKey:方法,其key是字符串类型,且key不能为nil,其value可以为nil,value为nil会自动调用removeObjectForKey:方法。其value支持的类型仅限于上面所提到的NSArray、NSDictionary、BOOL、NSData、NSDate、NSNumber、NSString。
注意,NSUserDefaults存储的对象均是其不可变对象,即使你创建的是可变对象,但NSUserDefaults实例存储的仍是它的不可变对象,取出的对象也是不可变对象。
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *mArr = [NSMutableArray array];
[mArr addObject:@"hello"];
[userDefaults setObject:mArr forKey:@"mArr"];
[userDefaults synchronize];
通过断点和po方法打印:
虽然我们存储的是NSMutableArray类型,但是实际取到的结果却是其不可变类型

利用NSUserDefaults存储自定义对象:
由于NSUserDefaults不支持自定义对象,但是可以看到支持NSData类型,我们可以将自定义对象转化为NSData对象,然后利用NSUserDefaults进行持久化存储。
- 归档
.h文件声明NSCoding协议,.m文件实现encodeWithCoder:和initWithCoder:方法,实际开发中我们可以利用如YYModel等第三方库类进行归档,不必这么麻烦的将模型中的每个属性都写一遍
Person.h:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
NS_ASSUME_NONNULL_END
Person.m:
#import "Person.h"
@implementation Person
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInteger:self.age forKey:@"age"];
}
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
self.name = [coder decodeObjectForKey:@"name"];
self.age = [coder decodeIntegerForKey:@"age"];
}
return self;
}
@end
- 存储
Person *person = [[Person alloc] init];
person.name = @"小明";
person.age = 18;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:data forKey:@"xiaoming"];
- 获取
NSData *result = [userDefaults objectForKey:@"xiaoming"];
Person *xm = [NSKeyedUnarchiver unarchiveObjectWithData:result];

如果存储数组,则可以将自定义对象放进数组中,然后进行存储:
NSArray *array = @[@{@"name":@"小明", @"age":@18}, @{@"name":@"小红", @"age":@19}, @{@"name":@"小新", @"age":@20}];
NSMutableArray *models = [NSMutableArray array];
for (NSDictionary *dicT in array) {
Person *person = [[Person alloc] init];
person.name = dicT[@"name"];
person.age = [dicT[@"age"] integerValue];
[models addObject:person];
}
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithArray:models]];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:data forKey:@"list"];
NSData *result = [userDefaults objectForKey:@"list"];
NSArray *list = [NSKeyedUnarchiver unarchiveObjectWithData:result];
网友评论