美文网首页
KVC非对象属性的表示以及属性的验证

KVC非对象属性的表示以及属性的验证

作者: 哆啦_ | 来源:发表于2018-05-09 20:54 被阅读18次

    非对象 value的表示

    NSObject提供的KVC协议方法的默认实现对对象和非对象属性都是有效的。默认的实现将自动转换对象参数或者返回值,以及非对象属性。这将允许基于keygetterssetters的特征保持一致,即使存储的属性是基本数据变量或结构体。

    因为Swift中所有的属性都是对象,这部分仅适用OC属性。

    当调用协议的getters方法时,比如valueForKey:,默认实现将根据KVC方法的搜索模式中描述的规则为指定的key提供特定访问方法或者实例变量。如果返回的值不是对象,则getters会将值初始化为NSNumber对象(对于基本数据类型)或者NSValue(对于结构体)返回。

    同样地,默认情况下,setter方法比如setValue:forKey:根据特定的key来确定属性的访问方法或者实例变量所需的数据类型。如果数据类型不是对象,setters首先向传入值(value)发送适当的<type>Value消息以获取底层数据,然后将其存储。

    注意:

    当你调用kvc协议中的一个方法为非对象属性设置一个 nil值时,它会向收到setter调用的对象发送一个setNilValueForKey:消息。该方法的默认实现会抛出NSInvalidArgumentException异常,但子类可以覆盖该方法。

    基本数据类型的Wrap 和 Unwrap

    下表列出了默认KVC实现中使用NSNumber实例包装的基本数据类型。

    Data type Creation method Accessor method
    BOOL numberWithBool: boolValue (in iOS) </br>charValue (in macOS)*
    char numberWithChar: charValue
    double numberWithDouble: doubleValue
    float numberWithFloat: floatValue
    int numberWithInt: intValue
    long numberWithLong: longValue
    long long numberWithLongLong: longLongValue
    short numberWithShort: shortValue
    unsigned char numberWithUnsignedChar: unsignedChar
    unsigned int numberWithUnsignedInt: unsignedInt
    unsigned long numberWithUnsignedLong: unsignedLong
    unsigned long long numberWithUnsignedLongLong: unsignedLongLong
    unsigned short numberWithUnsignedShort: unsignedShort

    结构体的Wrap 和 Unwrap

    Data type Creation method Accessor method
    CGPoint valueWithCGPoint: CGPointValue
    NSRange valueWithRange: rangeValue
    CGRect valueWithCGRect: CGRectValue
    CGSize valueWithCGSize: CGSizeValue

    当然,还有其他的结构体,比如UIEdgeInsets,CGVector以及我们自定义的结构体等等,这里就不一一列举了。

    验证属性

    KVC协议定义了支持属性验证的方法,就像使用基于key的访问方法来读写属性一样,我们也可以通过key(或keypath)来验证属性。 当调用validateValue:forKey:error:(或validateValue:forKeyPath:error:)方法,协议的默认实现是查找接收到验证消息的对象(或者keypath中结尾的对象)的名称匹配validate<Key>:error:的方法。如果对象没有该方法,默认情况下验证成功,当指定的属性的验证方法存在时,默认实现将返回该方法的结果。

    注意:

    通常仅在Objective-C中使用此处所述的验证。 在Swift中,属性验证通过依赖于编译器和强类型检查的支持来处理,同时使用内置的willSetdidSet属性观察器来测试任何运行时API.

    由于指定属性的验证方法通过引用valueerror参数,验证有三种可能的结果:

    1. 验证方法认为value对象有效并返回YES,(不会更改valueerror)
    2. 验证方法认为value对象无效,但并不改变它。这种情况,方法会返回NO并且将error参数(如果调用者提供了的话)设置为指示失败原因的NSError对象
    3. 验证方法认为value对象无效,并且新创建一个有效的作为替代。 这种情况下,该方法在error对象不变的情况下返回YES。在返回之前,该方法修改value的引用以指向新的value对象。当它进行修改时,该方法总是创建一个新的对象,而不是修改旧对象,即使value对象是可变的。

    validateValue:forKey:error:

    - (BOOL)validateValue:(inout id  _Nullable *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable *)outError;
    
    • ioValue: 指向由inKey标识的属性的新值的指针。 此方法可能会修改或替换该值以使其有效。
    • inKey: receiver的一个属性名称。必须是指定的Attribute或者一个to-one关系。
    • outError: 如果需要验证并且ioValue未转换为有效值,则在返回时包含描述ioValue不是有效值的原因的NSError对象。
    • 返回值: 如果ioValue指向的值对于由inKey标识的属性有效,或者该方法能够修改ioValue中的值使其有效,则返回YES; 否则为NO。
    validateValue:forKey:error:方法的默认实现

    该方法的默认实现是查找receiver类中方法名匹配-validate<Key>:error:的方法,如果你为属性定义了这样的方法,当验证相应的属性时,validateValue:forKey:error:的默认实现会调用-validate<Key>:error:方法。

    如果没有找到-validate<Key>:error:方法,validateValue:forKey:error: 返回 YES.

    下面示例展示了如何为name字符串调用验证的 示例:

      Person* person = [[Person alloc] init];
      NSError* error;
      NSString* name = @"John";
      BOOL validation = [person validateValue:&name forKey:@"name" error:&error];
      if (validation) {
          [person setValue:name forKey:@"name"];
      }
      else {
          NSLog(@"error: %@",error);
      }
    

    Person类实现了name的验证方法(这里设置了name必须是长度大于5的字符串)

    - (BOOL)validateName:(inout id  _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError
    {
        NSString *reason =  nil;
        if ([*ioValue isKindOfClass:[NSString class]]) {
            NSString *name = (NSString *)*ioValue;
            if (name.length > 5) {
                return YES;
            }
            else {
                reason = @"取名字必需大于5个字符";
            }
        }
        else {
            reason = @"name 必须是字符串";
        }
      if (outError != NULL) {
        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:reason};
        
        *outError = [NSError errorWithDomain:NSLocalizedDescriptionKey code:10086 userInfo:userInfo];
      }
        return NO;
    }
    
    validateValue:forKeyPath:error:
    - (BOOL)validateValue:(inout id  _Nullable *)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError * _Nullable *)outError;
    

    此方法的默认实现使用valueForKey获取每个关系的目标对象,并返回为该属性调用validateValue:forKey:error:方法的结果。

    自动验证

    通常,KVC协议及其默认实现都不定义任何自动执行验证的机制。相反,我们可以在app适当的时候使用验证方法。

    在某些情况下,某些其他Cocoa技术会自动执行验证。 例如,Core Data在保存管理对象上下文时自动执行验证(感兴趣的可以查看Core Data Programming Guide)。

    参考:

    相关文章

      网友评论

          本文标题:KVC非对象属性的表示以及属性的验证

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