美文网首页
setValuesForKeysWithDictionary的正

setValuesForKeysWithDictionary的正

作者: SpringAlways | 来源:发表于2020-04-19 09:12 被阅读0次
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues;
这个方法是NSObject的一个KVC分类。

对入参keyedValues,采取每一个<key, value>都调用 [self setValue: forKey:]的高阶函数调用,来修改原有dictionary中的<k, v>。

空值影响

如果<key, value>中的value为空,即NSNull, 则会调用[self setValue: nil forKey:]; key为空,则会崩溃。keyedValues为空,则不会有任何作用。

元素 为空影响
key crash
value crash
dic 不起任何作用
例一
  • 当keyedValues字典中value为NSNull时
@implementation Cat

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self setValue:@(11) forKey:@"_age"];
    }
    return self;
}
@end

- (void) test_setValuesForKeysWithDictionary
{
    Animal *b       = [Cat new];
    NSLog(@"b.age: %@", [b valueForKey:@"_age"]);
    [b setValuesForKeysWithDictionary:nil];
    NSLog(@"b.age: %@", [b valueForKey:@"_age"]);
    [b setValuesForKeysWithDictionary:@{@"_age" : @(12)}];
    NSLog(@"b.age: %@", [b valueForKey:@"_age"]);
    [b setValuesForKeysWithDictionary:@{@"_age" : [NSNull new]}];
    NSLog(@"b.age: %@", [b valueForKey:@"_age"]);
    [b setValuesForKeysWithDictionary:@{[NSNull new] : @(12)}];
    NSLog(@"b.age: %@", [b valueForKey:@"_age"]);
//    [b setValuesForKeysWithDictionary:@{@"_age1" : @(12)}];
//    NSLog(@"b.age: %@", [b valueForKey:@"_age1"]);
}
2020-05-18 16:01:22.952292+0800 TestDemo[18825:7239205] b.age: 11
2020-05-18 16:01:22.952332+0800 TestDemo[18825:7239205] b.age: 11
2020-05-18 16:01:22.952343+0800 TestDemo[18825:7239205] b.age: 12
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1
    frame #0: 0x00000001cba16cc4 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x00000001cc7172f4 CoreFoundation`+[NSException raise:format:] + 120
    frame #2: 0x00000001cd266750 Foundation`-[NSObject(NSKeyValueCoding) setNilValueForKey:] + 92
    frame #3: 0x00000001cd265d48 Foundation`_NSSetUsingKeyValueSetter + 132
    frame #4: 0x00000001cd1a937c Foundation`-[NSObject(NSKeyValueCoding) setValue:forKey:] + 268
  * frame #5: 0x00000001cd1f5084 Foundation`-[NSObject(NSKeyValueCoding) setValuesForKeysWithDictionary:] + 240
    frame #6: 0x000000010062679c TestDemo`-[YFKVX test_setValuesForKeysWithDictionary](self=0x0000000280d9c5d0, _cmd="test_setValuesForKeysWithDictionary") at YFKVX.m:61:5
    frame #7: 0x0000000100624c70 TestDemo`-[MyViewController viewDidLoad](self=0x0000000100d1dd50, _cmd="viewDidLoad") at MyViewController.m:143:5
    frame #8: 0x00000001f934ebb4 UIKitCore`-[UIViewController loadViewIfRequired] + 1028
    frame #9: 0x00000001f934efc0 UIKitCore`-[UIViewController view] + 32
    frame #10: 0x0000000100628244 TestDemo`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000000280f9d0e0, _cmd="application:didFinishLaunchingWithOptions:", application=0x0000000102300000, launchOptions=0x0000000000000000) at AppDelegate.m:23:8

当keyedValues字典中value为NSNull时,编译不会提示,运行时则crash _NSSetUsingKeyValueSetter -> [NSObject(NSKeyValueCoding) setNilValueForKey:]

  • 当keyedValues字典中的key为NSNull时


    image.png

当keyedValues字典中的key为NSNull时,则会编译时警告Object of type 'NSNull *' is not compatible with dictionary key type 'NSString *',运行时找不到方法-[NSNull lengthOfBytesUsingEncoding:]: unrecognized selector sent to instance 0x1fd187c00。这个是该kvc方法 Dictinary的泛型所致NSDictionary<NSString *, id>。而lengthOfBytesUsingEncoding是个什么方法呢?
它是NSString的Extension一个扩展。
Result in O(n) time; the result is exact. Returns 0 on error (cannot convert to specified encoding, or overflow)
文档中说明它会返回一个精确的字节值,如果为 0则说明发生了错误,或者编码问题,或者溢出。
所以,当- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;发生崩溃时,还有一种可能是编码方式出现了问题,或者溢出。因为,内部实现[NSObject(NSKeyValueCodingPrivate) _createValueSetterWithContainerClassID:key:]的key调用了lengthOfBytesUsingEncoding校验.

  • 设置不存在的值

设置不存在的值会怎么样呢?


image.png

可见,由于KVC在设置值的时候,找不到对应的key而崩溃。具体逻辑在_NSSetUsingKeyValueSetter

例二
- (void) test_setValuesForKeysWithDictionary2
{
    NSMutableDictionary *paras      = [NSMutableDictionary dictionary];
    [paras setValue:@(11) forKey:@"age"];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:nil];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:@{@"age" : @(12)}];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:@{@"age" : [NSNull new]}];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:@{@"_age" : @(12)}];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:@{[NSNull new] : @(12)}];
    NSLog(@"paras: %@", paras);
    [paras setValuesForKeysWithDictionary:@{@"_age1" : @(12)}];
    NSLog(@"paras: %@", paras);
}

输出是什么呢?可以先自己猜一下。

答案:

2020-05-18 15:58:22.817891+0800 TestDemo[18822:7238508] paras: {
    age = 11;
}
2020-05-18 15:58:22.817934+0800 TestDemo[18822:7238508] paras: {
    age = 11;
}
2020-05-18 15:58:22.817956+0800 TestDemo[18822:7238508] paras: {
    age = 12;
}
2020-05-18 15:58:22.817976+0800 TestDemo[18822:7238508] paras: {
}
2020-05-18 15:58:22.818000+0800 TestDemo[18822:7238508] paras: {
    "_age" = 12;
}
2020-05-18 15:58:22.818027+0800 TestDemo[18822:7238508] paras: {
    "_age" = 12;
    "<null>" = 12;
}
2020-05-18 15:58:22.818054+0800 TestDemo[18822:7238508] paras: {
    "_age" = 12;
    "<null>" = 12;
    "_age1" = 12;
}

可见上方表格中NSObject分类方法的规则已经被打破。

元素 为空影响
key nsnull被转化成@"<null>"
value 将原有dictionary中key对应的<key,value> entry删除
dic 不起任何作用

只有整体为空时,沿用nsobject扩展中的规则。


image.png image.png image.png

bl + mov 相当于c的函数调用和值返回,所以我们可以清楚地知道,setValuesForKeysWithDictionary方法在dictionary执行时调用了[NSDictionary dictionaryWithObjects:forKeys:count:] 并将结果返回给了调用方。
所以表现完全不一致了。

结论

用于NSObject时,和KVC的规则完全一致,但用于集合类型时,需要格外注意!我们只是分析了NSMutableDictionary,其他的集合类型想必也有对应于自身特性的不同表现。
自定义对象也可重写该方法。

相关文章

网友评论

      本文标题:setValuesForKeysWithDictionary的正

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