Objective-C:KVC

1 概述 数组

1.1 访问方法 app

      Key-value coding(KVC)是一种间接访问对象属性的机制,相似键值对,经过名字(或键)能够直接得到对象的属性值。事实上,key-value coding定义了一些模式和规则方法来实现属性的访问,这些基本方法在NSKeyValueCoding协议中声明,同时NSObject默认实现了该协议。 ide

KVC存在两种形式的访问方法: 性能

  • getter:获取对象的属性值
  • setter:设置对象的属性值

以下使用KVC来简化代码的编写: 测试

 1 - (id)tableView:(NSTableView *)tableview     // 未使用KVC的访问方法
 2  objectValueForTableColumn:(id)column
 3 row:( int)row
 4 {
 5     ChildObject *child = [childrenArray objectAtIndex:row];
 6     if ( [[column identifier] isEqualToString: @" name "] ) 
 7     {
 8        return [child name];
 9     }
10     if ( [[column identifier] isEqualToString: @" age "] )
11    {
12       return [child age];
13    }
14    if ( [[column identifier] isEqualToString: @" favoriteColor "] )
15   {
16      //  etc...
17   }
18   //  etc...
19  }
20 - (id)tableView:(NSTableView *)tableview    // 使用了KVC的访问方法
21      objectValueForTableColumn:(id)column
22                            row:( int)row
23 {
24       ChildObject *child = [childrenArray objectAtIndex:row];
25       return [child valueForKey:[column identifier]];
26 }

1.2 访问属性 编码

       Key-value coding技术其实就是间接访问对象的属性,支持KVC的属性有三种类型:attributes、to-one relationships和to-many relationships。 spa

        1) attributes:该类型属性是普通类型,有scalar、string和Boolean 等简单类型,以及一些值对象和不可变类型,如NSNumber和NSColor。 scala

        2) to-one relationship:该类型属性是对象类型,即该类型属性自己也是一个对象(拥有本身的属性)。 指针

        3) to-many relationships:该类型属性是一种容器,主要是NSArray 和NSSet容器。 code

2 KVC基础

2.1 键与键路径

1) 键(keys)

      键是字符串类型,用于标识对象的属性,建的命名必须遵循以下的规则:

      a) 必须是 ASCII编码的。

      b) 必须以小写字母打头。第一个字母能够是下划线,但不能是数字,也不能是大写字母。

      c) 不能包含空格。

2) 键路径(Key Paths)

      键路径也是一个键,其经过点操做符隔开不一样对象。访问一个属性的属性,就须要使用点标记指定一个更复杂的键路径。

好比:

1 [foo valueForKeyPath:@”someMember.someAttributeonMember”];
2 键路径访问 foo 对象的 someMember 特性,在 someMember 所属的类上查找someAttributeOnMember属性,并返回存储在这里的值。

2.2 Getter方法

      KVC提供以下方法来获取对象的属性值:

          1) valueForKey:方法

     经过向该方法传递一个key(标识符),可以得到对象的属性值。若没有相应的访问方法或没有健变量,则所属对象将调用自身的valueForUndefinedKey方法,该方法默认抛出NSUndefinedKeyException异常

         2) valueForKeyPath:方法

      经过向该方法传递一个key path,可以得到对象中属性的属性值。若没有键路径,一样会调用自身的valueForUndefinedKey方法

         3) dictionaryWithValuesForKeys:方法

      用一个标识符数组(字符串类型)返回一个NSDictionary对象。

2.3 Setter方法

KVC提供一系列方法设置对象的属性:

         1) setValue:forKey:方法

     经过向该方法传递key和value,从而可以设置对象的属性值,而且该方法自动实现wrapping和unwrapping。若对象中不存在key标识符或变量,则会调用该对象的setValue:forUndefinedKey:方法,该方法的默认实现是抛出NSUndefinedKeyException类型的异常。

        2) setValue:forKeyPath:方法

     一样是设置对象的属性,不一样的是该方法可以操纵key path。

        3) setValuesForKeysWithDictionary:方法

     经过向该方法传递一个NSDictionary对象,从而设置对象的属性。 

2.4 访问实例

       以下简单建立Myclass类,并在该类中定义了两个属性,接着经过KVC进行访问对象的属性。

1) 类定义

@interface MyClass : NSObject
@property NSString * string;
@property NSInteger integer;
@property MyClass *instance;
@end

2) 键访问

 1  int main( int argc,  const  char * argv[])
 2  {
 3     MyClass *myInstance = [[MyClass alloc] init];
 4     
 5     [myInstance setValue: @" hello world " forKey: @" string "];
 6     [myInstance setValue:[NSNumber numberWithInt: 100] forKey: @" integer "];
 7     
 8     NSLog( @" %@ ",[myInstance valueForKey: @" string "]);
 9     NSLog( @" %@ ",[myInstance valueForKey: @" integer "]);
10      return  0;
11 }
12 输出:
13        2016- 04- 06  21: 04: 32.533 KVC[ 1134: 94328] hello world
14        2016- 04- 06  21: 04: 32.534 KVC[ 1134: 94328100

3) 键路径访问

 1  int main( int argc,  const  char * argv[]) 
 2 {
 3     MyClass *myInstance = [[MyClass alloc] init];
 4     MyClass *anotherInstance = [[MyClass alloc] init];
 5     anotherInstance.instance = myInstance;
 6     
 7     [anotherInstance setValue: [NSNumber numberWithInt: 222]  forKeyPath: @" instance.integer "];
 8     NSLog( @" %@ ",[anotherInstance valueForKeyPath: @" instance.integer "]);
 9     
10      return  0;
11 }
12 输出:
13       2016- 04- 06  21: 13: 46.432 KVC[ 1253: 99388222

3 存取方法

      虽然可使用KVC中的访问方法valueForKey:和setValue:forKey:来操做对象的属性,但须要实现属性的存取方法来供KVC访问方法调用

3.1 普通模式

3.1.1 取方法(getter)

      对于属性的取方法有两种形式,对于不一样类型的属性能够实现以下两种之一:

  • -<key>此种形式的取方法用于返回一个对象、标量或结构体等类型。
  • -is<Key>此种形式的取方法用于返回一个布尔类型的值。

     如某类中的hidden属性为bool类型,能够以下两种方式实现其取方法:

 1 - (BOOL)hidden
 2 {
 3      //  Implementation specific code.
 4      return ...;
 5 }
 6 - (BOOL)isHidden
 7 {
 8     //  Implementation specific code.
 9     return ...;
10 }

3.1.2 存方法(setter)

       为了使用KVC的setValue:forKey:方法来设置属性值,须要实现对象的set<Key>:存方法。以下所示hidden属性的存方法实现。

1 - ( void)setHidden:(BOOL)flag
2 {
3  //  Implementation specific code.
4    return;
5 }

       若设置的属性值为non-object类型(即为标量),那么还需考虑传递给存方法的参数为nil的状况。其中在Objective-C中,若给标量属性的存方法传递nil参数,那么将调用该对象的setNilValueForKey:方法。从而工程师能够重载该方法来从新设置新的值,不然将抛出异常。

      如在给setHidden方法传递nil参数时,须要将其重定向为YES值,那么能够进行以下重载setNilValueForKey:方法:

1 - ( void)setNilValueForKey:(NSString *)theKey
2 {
3      if ([theKey isEqualToString: @" hidden "]) 
4     {
5         [self setValue:[NSNumber numberWithBool:YES] forKey: @" hidden "];
6     }  else
7     [super setNilValueForKey:theKey];
8 }

3.2 集合模式

    若对象的属性是一个集合,也能够简单就实现-<key>和-set<Key>:两种存取方法来访问集合对象。若须要访问集合中的元素,那么须要实现更多的方法。

    能够简单将Objective-C的集合类型分为两种:有序无序。从而集合元素的存取方法也相应有两种形式:索引存取器方法和无序存取器方法。

3.2.1 有序集合

      索引方法定义了集合属性的计数、查询、添加和替换等机制,其中有序集合有种两种类型:NSArray和NSMutableArray

1) 取元素方法(不可变集合)

      不可变集合只能获取集合中的元素,从而只需实现以下的方法:

  • -countOf<Key>必须实现,此方法的功能相似NSArray类的count方法。
  • -objectIn<Key>AtIndex:-<key>AtIndexes:必须实现之一,一样相似于NSArray类的objectAtIndex: and objectsAtIndexes:方法。
  • -get<Key>:range:可选,其功能相似于NSArray类的getObjects:range方法。

      如在某类中有个employees集合,从而可实现上述四个方法:

 1 - (NSUInteger)countOfEmployees
 2 {
 3       return [employees count];
 4 }
 5 - (id)objectInEmployeesAtIndex:(NSUInteger)index
 6 {
 7       return [employees objectAtIndex:index];
 8 } -
 9 (NSArray *)employeesAtIndexes:(NSIndexSet *)indexes
10 {
11       return [employees objectsAtIndexes:indexes];
12 }
13 - ( void)getEmployees:(Employee **)buffer range:(NSRange)inRange
14 {
15      //  Return the objects in the specified range in the provided
16      //  buffer. For example, if the employees were stored in an
17      //  underlying NSArray
18      [employees getObjects:buffer range:inRange];
19 }

 

2) 存元素方法(可变集合)

       对于可变集合(mutable)自己是能够修改集合的元素,所以除了须要实现上述的取元素方法,同时还须要实现以下的存元素方法:

          a) -insertObject:in<Key>AtIndex: -insert<Key>:atIndexes:

         至少实现之一,其功能相似于NSMutableArray类的insertObject:atIndex和insertObjects:atIndexes:。

          b) -removeObjectFrom<Key>AtIndex: -remove<Key>AtIndexes:

        至少实现之一,其功能相似于NSMutableArray类的removeObjectAtIndex:和removeObjectsAtIndexes:。

          c)-replaceObjectIn<Key>AtIndex:withObject: -replace<Key>AtIndexes:with<Key>:

        可选的,如有性能上的要求能够实现其。

     如在某类中有个employees集合,从而可实现上述6个方法:

 1 - ( void)insertObject:(Employee *)employee  inEmployeesAtIndex:(NSUInteger)index
 2 {
 3      [employees insertObject:employee atIndex:index];
 4      return;
 5 } 
 6 -( void)insertEmployees:(NSArray *)employeeArray atIndexes:(NSIndexSet *)indexes
 7 {
 8      [employees insertObjects:employeeArray atIndexes:indexes];
 9      return;
10 }
11 - ( void)removeObjectFromEmployeesAtIndex:(NSUInteger)index
12 {
13     [employees removeObjectAtIndex:index];
14 }
15 - ( void)removeEmployeesAtIndexes:(NSIndexSet *)indexes
16 {
17     [employees removeObjectsAtIndexes:indexes];
18 }
19 - ( void)replaceObjectInEmployeesAtIndex:(NSUInteger)index withObject:(id)anObject
20 {
21     [employees replaceObjectAtIndex:index withObject:anObject];
22 } 
23 -( void)replaceEmployeesAtIndexes:(NSIndexSet *)indexes withEmployees:(NSArray *)employeeArray
24 {
25     [employees replaceObjectsAtIndexes:indexes withObjects:employeeArray];
26 }

3.2.2 无序集合

1) 取元素方法(不可变集合)

       对于无序的集合来讲,通常的取元素方法是获取迭代器和判断是否为成员对象。为了知足只读须要,必须实现以下全部的方法。

  • -countOf<Key>相似NSSet类的count方法;
  • -enumeratorOf<Key>相似NSSet类的objectEnumerator方法;
  • -memberOf<Key>相似NSSet类的member方法;

以下是实现例子:

 1 - (NSUInteger)countOfTransactions
 2 {
 3      return [transactions count];
 4 } 
 5 -(NSEnumerator *)enumeratorOfTransactions
 6 {
 7      return [transactions objectEnumerator];
 8 }
 9 -(Transaction *)memberOfTransactions:(Transaction *)anObject
10 {
11      return [transactions member:anObject];
12 }

2) 存元素方法(可变集合)

      为了容易且高效的利用mutableSetValueForKey:方法来访问无序集合的元素,须要实现以下方法:

         a) -add<Key>Object: -add<Key>:

         至少实现二者之一,功能相似于NSMutableSet类的addObject:方法。

         b) -remove<Key>Object: -remove<Key>:

         至少实现二者之一,功能相似于NSMutableSet类的removeObject:方法。

         c) -intersect<Key>:

        可选类型,功能相似于NSSet 类的intersectSet: 方法。

以下的测试例子:

 1 - ( void)addTransactionsObject:(Transaction *)anObject
 2 {
 3     [transactions addObject:anObject];
 4 }
 5 -( void)addTransactions:(NSSet *)manyObjects
 6 {
 7     [transactions unionSet:manyObjects];
 8 }
 9 - ( void)removeTransactionsObject:(Transaction *)anObject
10 {
11     [transactions removeObject:anObject];
12 } 
13 -( void)removeTransactions:(NSSet *)manyObjects
14 {
15     [transactions minusSet:manyObjects];
16 }
17 - ( void)intersectTransactions:(NSSet *)otherObjects
18 { 
19     return [transactions intersectSet:otherObjects];
20 }

4 搜索模式

4.1 简单属性

4.1.1 默认setValue:forKey

当为了设置对象的某个属性,而调用setValue:forKey:方法时,将按以下顺序执行搜素:

     1) 首先会搜索接收对象的set<Key>:方法,即setValue:forKey:方法所属的对象。

    2) 若未找到匹配的方法,且接收对象的accessInstanceVariablesDirectly方法会返回YES,那么将在接收对象中按序搜素以这样命名的实例变量:_<key>_is<Key><key>、和is<Key>

     3) 若找到匹配的方法,那么会调用匹配的方法;若须要能够设置Non-Object值。

     4) 若没有匹配方法或实例变量被发现,那么会调用接收对象的setValue:forUndefinedKey:方法

4.1.2 默认valueForKey

当为了访问对象的某个属性,而调用valueForKey:方法时,将按以下的顺序执行搜索:

     a) 首先会在接收对象中按序搜索get<Key><key>、和is<Key>方法,若找到其中之一的方法,而且该方法返回的是Objective-C类型的指针,则直接返回;如果找到的方法是返回支持NSNumber的标量类型,那么将执行转换。

     b) 若未找到匹配的方法,则在接收对象中按序搜素countOf<Key>objectIn<Key>AtIndex:<key>AtIndexes:方法

     c) 若仍未找到匹配的方法,则在接收对象中按序搜索countOf<Key>enumeratorOf<Key>、和memberOf<Key>:方法

    d) 若未找到匹配的方法,且接收对象的accessInstanceVariablesDirectly方法会返回YES,那么将在接收对象中按序搜素以这样命名的实例变量:_<key>_is<Key><key>、和is<Key>

     e) 若上述全部状况都未发现,那么将调用接收对象的valueForUndefinedKey:方法

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息