KVC协议定义了通过key
或key path
来验证属性的方法。这些方法的默认实现反过来依赖与你定义的遵循访问器方法命名规则的方法。具体来说,你为任何一个你想要验证的名为key
的属性提供一个validate<Key>:error:
方法。 默认实现是通过键编码的validateValue:forKey:error:
消息来搜索的。
如果未提供属性的验证方法,该协议的默认实现假定该属性的验证成功,而不管该值如何。 这意味着你选择逐个属性的验证。
通常仅在Objective-C中使用此处所述的验证。 在Swift中,属性验证通过依赖于编译器和强类型检查的支持来处理,同时使用内置的willSet和didSet属性观察器来测试任何运行时API.感兴趣的同学可以查看Property Observers In Swift
验证方法的实现
当为一个属性提供验证方法时,该方法由两个参数:要验证的value
对象和用于返回错误信息的NSError
。因此,验证方法可以采取以下三种操作之一:
- 当
value
对象有效时,返回YES,(不更改value
或者error
) - 当
value
对象无效时,并且你不能或不想提供有效的替代方法,将参数error
设置为指示失败原因的NSError对象,并返回NO。IMPORTANT
在设置
error
之前需要测试error
参数不为NULL
- 当
value
对象是无效的,但是你知道一个有效的替代方法,创建一个有效的对象,将value
的引用指定到新对象,并返回YES(不修改error
引用)。如果你提供了其他的value
,则始终返回一个新的对象而不是修改正在验证的对象,即使原始对象是可变的。
下面的代码演示了name
属性的验证方法,该方法确保value
不是nil
,并且名称有一个最小长度。 如果验证失败,此方法不会用另一个值替代。
- (BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2)) {
if (outError != NULL) {
*outError = [NSError errorWithDomain:PersonErrorDomain
code:PersonInvalidNameCode
userInfo:@{ NSLocalizedDescriptionKey
: @"Name too short" }];
}
return NO;
}
return YES;
}
上面我们说了,在设置error
之前必须检测error
是否为NULL
,如果我们没有检测error
,而是直接设置error
,如下所示:
- (BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 5)) {
NSErrorDomain PersonErrorDomain = NSCocoaErrorDomain;
NSInteger PersonInvalidNameCode = 10086;
// 不检测error是否为`NULL`
*outError = [NSError errorWithDomain:PersonErrorDomain
code:PersonInvalidNameCode
userInfo:@{ NSLocalizedDescriptionKey
: @"Name too short" }];
return NO;
}
return YES;
}
调用检测方法
Person* person = [[Person alloc] init];
NSString* name = @"A";
BOOL validation = [person validateValue:&name forKey:@"name" error: nil];
if (validation) {
[person setValue:name forKey:@"name"];
}
这时就会抛出野指针:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
基本数据变量的验证
验证方法期望value
参数是一个对象,因此,非对象属性的值将包含在NSValue或NSNumber对象中,如这篇文章所述。 表11-2中的例子演示了基本数据变量age
的验证方法。 在这种情况下,通过设置一个有效的@0
值来处理一个value
为nil
的情况,并返回YES。 我们也可以在setNilValueForKey:
方法中来处理这种情况,毕竟有时候我们不会调用验证方法。
示例:
- (BOOL)validateAge:(id *)ioValue error:(NSError * __autoreleasing *)outError {
if (*ioValue == nil) {
// Value is nil: Might also handle in setNilValueForKey
*ioValue = @(0);
} else if ([*ioValue floatValue] < 0.0) {
if (outError != NULL) {
*outError = [NSError errorWithDomain:PersonErrorDomain
code:PersonInvalidAgeCode
userInfo:@{ NSLocalizedDescriptionKey
: @"Age cannot be negative" }];
}
return NO;
}
return YES;
}
网友评论