OC中键值编程(KVC)的实现

原文:http://disanji.net/2010/12/12/5-key-value-coding-approaches-in-cocoa-html/

Key-value coding(KVC)是用来使得一个类的行为摆脱它需要依附执行的特定属性的一种方式。它通常与NSKeyValueCoding协议联系在一起当然也有一系列其他的方法来实现相同的效果。在这篇文章中,我关注为什么KVC是重要的而且要介绍5种不同的方式来实现这个功能。

介绍

NSKeyValueCoding协议自从Mac OS X10.0起就已经在Cocoa中,但是它给人留下深刻印象是在Mac OS X10.3中当用户接口绑定展示了他们的潜力时——允许用户接口通过配置数据而不用更改代码就可以控制连接到对象的属性。

KVC的核心概念存在于大多数的编程框架中,它是流行而且普遍的在Cocoa中,当然也在其他的编译程序框架中。

尽管它很流行,你还是可以避免使用KVC如果你愿意。然而,Apple引入它是有原因的,它作为一个简单的设计模式可以使你在编程时受益。下面,我将讲述到底key-value coding(KVC)是什么,它如何改进你的代码以及不同的方式来实现这个效果。

什么是key-value coding?

KVC的思想是相当简单的,不用直接获取或设置一个对象的属性,KVC通过传递一个”key”(通常是一个字符串)来获取或更改与这个”key”相关的属性。这个听起来非常像NSDictionary

例如:

1
2
3
4
5
// Set a property directly...
someObject.someProperty = someValue;
 
// ...or set the same property using key-value coding
[someObject setValue:someValue forKey:@"someProperty"];

为什么你要这样做?答案是它简化了设置某个属性值的行为。

设想有这样一个可以编辑姓名和地址的表格:

没有KVC方式,利用NSTableViewDataSource方法来编辑某一行也许要这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)tableView:(NSTableView *)aTableView
    setObjectValue:(NSString *)anObject
    forTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
{
    if ([[aTableColumn identifier] isEqual:@"name"])
    {
        [[records objectAtIndex:rowIndex] setName:anObject];
    }
    else if ([[aTableColumn identifier] isEqual:@"address"])
    {
        [[records objectAtIndex:rowIndex] setAddress:anObject];
    }
}

而利用KVC方式,这个方法变成:

1
2
3
4
5
6
7
- (void)tableView:(NSTableView *)aTableView
    setObjectValue:(NSString *)anObject
    forTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
{
    [[records objectAtIndex:rowIndex] setValue:anObject forKey:[aTableColumn identifier]];
}

前面我提到了绑定(bindings):这个方法并没有使用用户接口绑定。但这也确实展示了一个简化的实现绑定的方式。

这个KVC方法是更好的因为它不必编辑每一个分立的属性。这就是KVC的精华之所在。

当数据量增加时,KVC愈发体现它的高效。一个有1000列的表格其实只需要与上述相同量的代码就可以实现编辑。

KVC方法1:NSKeyValueCoding protocol

我目前展示的所有KVC方式都是利用NSKeyValueCoding协议。其实,我称它为协议是因为它确实是一个通俗的协议(NSObject中一个分类)。

这个分类继承setValue:forKey: 和valueForKey:两个方法使你可以设置和获取值通过NSString的keys。

优点:

  • 自动找到已经存在的获取和设置的方法,而且如果该方法不存在,就直接获取或设置值。这意味这大多数的属性都能自动支持NSKeyValueCoding。可以在我的文章Key Value Information post.中学习更多关于此的内容。
  • 包含key的路径(为了传递多个属性)。
  • 集成了NSKeyValueObserving来实现观察设计实例。
  • 当key没有定义时提供后续处理方法。

缺点:

继承的搜索路径使这种方法是最的KVC方法(可以阅读我前面相关的文章Replacing Core Data Key Paths)。

要求这个类中有个相关方法或者有变量与这个名字的属性相关,而且可以被NSKeyValueCoding找到。

仅仅支持NSStrings作为属性的“key”。

KVC 方法2: 手动设置NSKeyValueCoding 行为的子集

NSKeyValueCoding根据选择器来查找方法,根据名字查找成员。

这些工作需要你首先进行设置。

1
2
3
4
5
6
// Manual KVC setter method implementation
NSString *setterString = [@"set" stringByAppendingString:[someKeyString capitalizedString]];
[someObject performSelector:NSSelectorFromString(setterString) withObject:someValue];
 
// Manual KVC ivar setter
object_setInstanceVariable(someObject, someKeyString, someValue);

为什么你要这样做而不是使用NSKeyValueCoding方法呢?当你要避免使用NSKeyValueCoding默认找到的方法或成员时你可以使用这个方法。这个方法允许你自定义查找路径。

优点:

  • 相比NSKeyValueCoding更多的设置查找路径。
  • 潜在地比NSKeyValueCoding要快。
  • 可以为不是继承于NSObject的类工作因此不用继承NSKeyValueCoding。
  • 手动实现可以获得与设置非对象的值。

缺点:

  • 不如NSKeyValueCoding灵活。
  • 在大多数情况下,它需要更大的工作量。
KVC 方法3: 关联对象

Objective-C 2.0执行时(在iPhone或64位Mac OS X程序上)允许设置一个对象关联到任何其他对象。这使得执行时任何对象都可以通过key来任意被设置额外的属性,而不需要来自这个对象的方法或成员支持。

objc_setAssociatedObject(someObject, someKey, someValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

你会用到这个方法的主要原因是你打算在外部设置对象的属性—例如,不用对象支持,被包含于甚至不知道这个属性的路径。某个属性可以被程序的其它部分根据需要来设置到一个对象上面。

优点

  • 不需要一个对象支持。
  • Key可以是任意指针(也可以是实体只要使用OBJC_ASSOCIATION_ASSIGN)。
  • 有可能是最快的KVC方法

缺点

  • Key是一个指针而不是实体,因此如果使用实体,它必须是具有唯一指针的实体(即如果你打算通过一个字符串值的不同分配空间来获取与设置值,那是不可以的)。
  • 不会影响对象内部的方法或变量。这表示如果你打算让这个对象了解在它本身发生的事情,你就不得不使用一个不同的方法。
KVC 方法 4: 将选择器作为keys

KVC根本上来说是通过Key来查找一个属性,之后在这个属性上进行操作。

Objective-C在它的内核有一个检查—方法查找。这个查找的keys就是选择器。

objc_msgSend(someObject, someSetterSelector, someValue);

这个方法与手动设置NSKeyValueCoding方法部分类似,但是它不是形成一个选择器的字符串来作为key,这个方法直接利用选择器作为key。

这个方法的缺点是需要为获取和设置分别设置选择器。

优点

  • 最快的方式来遍历查找方法(由于方法可以重写因此更加化为子类更加方便)
  • 能够获取和设置非对象数据(需要objc_msgSend_fpret 和 objc_msgSend_stret 来得到 float型,double型以及struct型的属性)。

缺点

  • 获取和设置需要不同的keys。
  • 选择器不是实体一次不能直接在Objective-C数组中存储(必须使用CoreFoundation 或者 NSValuewrappers)。
KVC 方法5: 自己实现它

最后一个方法是自己处理这个实现。如果你需要最大的灵活性的话(处理特殊的keys或者数据)或者想要暴露一个对象实体的不同的关键值。

最简单的实现这一条的方式是暴露获取和设置方法,之后在对象包含的dictionary上获取和设置这些值。

1
2
3
4
5
6
7
8
9
- (void)setCollectionValue:(id)value forKey:(NSString *)key
{
    [collectionDictionary setObject:value forKey:key];
}
 
- (id)getCollectionValueForKey:(NSString *)key
{
    return [collectionDictionary objectForKey:key];
}

你可以使用Cocoa中任何一种关键值存储结构来处理这些值内部存储:

  • NSMutableDictionary
  • NSMapTable
  • CFMutableDictionaryRef
  • associated objects on selfor other objects (see above)

或者你自己的存储方案。

优点

  • 一个独立的对象能够暴露多种的分类收集。
  • 能够获取以及设置任何被这个收集所支持的数据类型。
  • 能够为特殊的情况提供最灵活的后续方法。

缺点

  • 必须在target class上实现(不能支持随意对象)。
  • 不能与NSKeyValueObserving或者NSKeyValueObserving的相关概念交互操作。

结论

使用key-value coding并不是必需的。实现整个工程不用KVC当然是可能的。但是,它是最好的代码样式之一,可以减少重复代码以及使得类可以复用。

你可以看到,有许多不同的KVC编程方法。NSKeyValueCoding可能是最灵活的,复用的而且能够很好的被Cocoa支持的方式,因此除非你需要上面提到的其他方法的优点之一或者你打算使你的方案限定解决特定的问题,这个方法可能是最好的方式。

作者:Matt Gallagher

原文链接:http://cocoawithlove.com/2010/01/5-key-value-coding-approaches-in-cocoa.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值