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:方法

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息