美文网首页
KVC之定义集合方法

KVC之定义集合方法

作者: 哆啦_ | 来源:发表于2018-05-11 23:15 被阅读12次

    当你使用这篇文章描述的标准约定创建一个访问方法和ivar时,KVC协议的默认实现可以根据KVC消息定位它们。对于表示to-many关系的集合对象来说也是如此。但是,如果你实现了集合访问方法,我们就可以:

    • 与除了NSArray或NSSet以外的类建立to-many关系.当我们实现了集合方法,key-value getter的默认实现是返回一个代理对象,该对象调用这些方法以响应后续收到的NSArrayNSSet消息。属性对象不必是NSArray或者NSSet,因为代理对象使用集合方法提供预期的行为。

    • 改变to-many关系的内容可以提高性能. 协议的默认实现是使用你的集合方法来改变基础属性,而不是重复创建新的集合对象。

    • 为集合属性中的内容提供KVO访问 更多内容可以查看 Key-Value Observing Programming Guide.

    你可以实现两类集合访问方法中的一个,具体取决于我们希望关系的行为像索引的有序集合(如NSArray对象)还是无序的、但唯一的集合(如NSSet对象)。任何情况下,需要至少实现一组方法来支持对属性的读取访问。

    KVC协议不会声明在这部分描述的的方法。相反,NSObject提供的协议的默认实现是在你的KVC兼容对象中查找这些方法,如KVC方法的搜索模式所述,并使用它们来处理部分KVC消息。

    访问有序集合

    你可以添加索引访问方法,以提供计算、检索、添加和替换有序关系中的对象的机制。底层对象通常是NSArrayNSMutableArray对象,但是如果为对象提供了集合访问方法,我们可以像处理数组一样处理这些属性。

    有序集合的getters

    对于没有默认的getter的集合属性。如果你提供以下索引集合方法,则对于valueForKey:消息,协议的默认实现是返回一个代理对象,其行为类似NSArray,但是会调用以下集合方法以完成它的工作。

    在现在的OC中,编译器为每个属性默认生成了getter,因此默认实现不会创建使用本节的方法。可以通过不声明属性(仅依靠一个ivar),或者你可以使用@dynamic声明一个属性来解决这个问题,这表明我们计划在运行时提供访问者行为。无论何种方式,编译器都不会提供默认的getter,而默认的实现会使用下面的方法

    • countOf<Key>

      此方法以NSInteger 的形式返回to-many 关系中对象的个数,就像NSArraycount方法一样。事实上,当底层属性是NSArray时,可以使用该方法提供结果。

      - (NSUInteger)countOfTransactions {
          return [self.transactions count];
      }
      
    • objectIn<Key>AtIndex: or <key>AtIndexes:

      第一个方法返回 to-many 关系中指定下标中的对象,第二个返回一个NSIndexSet参数指定索引处的对象数组。 它们分别对应于NSArrayobjectAtIndex:objectsAtIndexes:。 只需要实现它们中的一个。transactions数组的相应方法是:

      - (id)objectInTransactionsAtIndex:(NSUInteger)index {
      return [self.transactions objectAtIndex:index];
      }
      
      - (NSArray *)transactionsAtIndexes:(NSIndexSet *)indexes {
      return [self.transactions objectsAtIndexes:indexes];
      }
      
    • get<Key>:range:

      该方法是可选的,但可以提高性能。它返回集合指定范围内的对象,对应于NSArraygetObjects:range:方法,transactions数组的相应方法是:

      - (void)getTransactions:(Transaction * __unsafe_unretained *)buffer
                   range:(NSRange)inRange {
                   
        [self.transactions getObjects:buffer range:inRange];
      }
      

    比如在一个Person类中,定义了一个gender属性(NSString对象),这里我们在.m文件为其实现相应的集合方法,这样gender就会表现的跟一个数组一样。

    @interface Person : NSObject
    @property (nonatomic , copy) NSString *gender;
    @end
    
    @implementation Person
    // 使用@dynamic修饰,所以编译器不会为其生成getter和setter
    @dynamic gender;
    
    - (NSUInteger)countOfGender
    {
        return 5;
    }
    - (id)objectInGenderAtIndex:(NSUInteger)index
    {
        return @"w";
    }
    @end
    
     id obj = [person valueForKey:@"gender"];
     NSString *cls = NSStringFromClass([obj class]);
     NSLog(@"gender: %@", obj);
     NSLog(@"className:%@",cls);
    

    打印信息为

    gender: (
    w,
    w,
    w,
    w,
    w
    )
    className:NSKeyValueArray

    可以看出,虽然我们声明的是NSString,但由于我们实现了属性相应的集合方法,这里属性已经变成了NSArray类型。(而且都是不可变数组,即使我们声明的是NSMutableArray类型)

    有序集合的改变

    使用索引方法来实现可变的 to-many 的关系必须实现一组不同的方法。当你提供这些setter方法时,mutableArrayValueForKey:消息的默认实现是返回一个与NSMutableArray行为相似的代理对象。这比直接返回一个NSMutableArray对象更加有效率。

    为了使键值编码对象表现跟可变、有序的 to-many 关系一样,需要实现以下这些方法:

    • insertObject:in<Key>AtIndex: or insert<Key>:atIndexes:

      第一个方法接收要插入的对象和指定应该插入的位置。第二个方法插入一个对象数组到NSIndexSet指定的索引处的集合中。这跟NSMutableArrayinsertObject:atIndex:insertObjects:atIndexes:方法类似。只需要其中一个方法。

      - (void)insertObject:(Transaction *)transaction
      inTransactionsAtIndex:(NSUInteger)index {
      [self.transactions insertObject:transaction atIndex:index];
      }
      
      - (void)insertTransactions:(NSArray *)transactionArray
               atIndexes:(NSIndexSet *)indexes {
      [self.transactions insertObjects:transactionArray atIndexes:indexes];
      }
      
    • removeObjectFrom<Key>AtIndex: or remove<Key>AtIndexes:

      第一个接收一个NSUInteger值,第二个接收一个NSIndexSet对象,指定要删除的对象的索引。 这些方法分别对应于NSMutableArray方法removeObjectAtIndex:removeObjectsAtIndexes:。 只需要其中一种方法。

      - (void)removeObjectFromTransactionsAtIndex:(NSUInteger)index {
       [self.transactions removeObjectAtIndex:index];
      }
      
      - (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
       [self.transactions removeObjectsAtIndexes:indexes];
      }
      
    • replaceObjectIn<Key>AtIndex:withObject:replace<Key>AtIndexes:with<Key>:

      为代理对象提供了一种直接替换集合中对象的方法,而无需连续删除一个对象并插入另一个对象。对应于NSMutableArrayreplaceObjectAtIndex:withObject:replaceObjectsAtIndexes:withObjects:方法。当应用程序分析显示性能问题时,可以选择提供这些方法。

      - (void)replaceObjectInTransactionsAtIndex:(NSUInteger)index
                                 withObject:(id)anObject {
        [self.transactions replaceObjectAtIndex:index
                                  withObject:anObject];
      }
      
       - (void)replaceTransactionsAtIndexes:(NSIndexSet *)indexes
                        withTransactions:(NSArray *)transactionArray {
        [self.transactions replaceObjectsAtIndexes:indexes
                                    withObjects:transactionArray];
      }
      

    访问无序集合

    通常,这种关系是NSSetNSMutableSet对象的一个实例。 但是,实现这些方法时,我们可以使用键值编码对该对象进行操作,就像它是NSSet的实例一样。

    无序集合的getter

    当你提供以下集合方法以返回集合中对象的数量的时候,valueForKey:消息返回一个跟NSSet行为类似的代理对象,但是是调用下面的集合方法来完成。

    • countOf<Key>

      该必需方法返回关系中条目的数量,对应NSSetcount方法。当底层对象是NSSet对象,会直接调用count方法。比如

       - (NSUInteger)countOfEmployees {
         return [self.employees count];
       }
      
    • enumeratorOf<Key>

      这个必需方法返回一个NSEnumerator实例,用于遍历关系中的条目。

      - (NSEnumerator *)enumeratorOfEmployees {
       return [self.employees objectEnumerator];
      }
      
    • memberOf<Key>:

      这个方法将作为参数传过来的对象和集合中的内容进行比较,并返回匹配的对象,如果没有找到匹配的对象返回nil。如果要手动实现比较方法,通常是使用isEqual:来比较对象。如果底层对象是NSSet对象,可以使用member:方法

      - (Employee *)memberOfEmployees:(Employee *)anObject {
      return [self.employees member:anObject];
      }
      

    无序集合的改变

    为了使可变无序的to-many关系的支持键值编码,需要实现以下方法:

    • add<Key>Object: or add<Key>:

    添加单个或多个对象。当添加多个对象时,请确保关系中不存在同等的对象。这与NSMutableSetaddObject:unionSet:方法类似。 只需要其中一个方法:

     - (void)addEmployeesObject:(Employee *)anObject {
     [self.employees addObject:anObject];
     }
    
     - (void)addEmployees:(NSSet *)manyObjects {
     [self.employees unionSet:manyObjects];
     }
    
    • remove<Key>Object: or remove<Key>:

      从关系中删除单个或者多个对象。这与NSMutableSetremoveObject:minusSet:类似。 只需要其中一个方法:

      - (void)removeEmployeesObject:(Employee *)anObject {
       [self.employees removeObject:anObject];
      }
      
      - (void)removeEmployees:(NSSet *)manyObjects {
       [self.employees minusSet:manyObjects];
      }
      
    • intersect<Key>:

      这个方法接收一个NSSet作为参数,从关系中删除不为输入集与集合中共有的所有对象。这跟NSMutableSetintersectSet:方法等效。该方法是可选的。

      - (void)intersectEmployees:(NSSet *)otherObjects {
        return [self.employees intersectSet:otherObjects];
      }
      

    相关文章

      网友评论

          本文标题:KVC之定义集合方法

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