2010年11月05
脚注
[1]现有的值也可以是原语等 BOOL或 int,因为现有的取值和赋值方法将执行自动装箱。例如,一个 BOOL值将被auto-boxed成一个NSNumber*.
[2]当我说绑定同步两个关键路径,这不是技术上正确的。它实际上要同步“绑定”和一个关键路径。“绑定”是一个字符串,就像一个关键路径但不保证现有的兼容,虽然它可以。请注意,该示例代码使用 @"address"但从未使用作为一个关键路径 @"value"作为一个关键路径。这是因为 @"value"是一个绑定,它可能不是一个有效的关键路径。
Cocoa绑定可能有点混乱,特别是新人。一旦你了解底层概念,绑定并不太难。在这篇文章中,我将解释背后的概念绑定的,首先解释了键值编码(现有的),然后观察(KVO)键值,最后说明可可绑定是建立在知识和KVO。
键值编码(KVC)
第一个你需要了解的概念键值编码(KVC),KVO和绑定是建立其基础之上的。
对象有一定的“属性”。例如,一个 Person对象可能一个 name property和一个 address property。在KVC中, Person对象有一个值 name键,和 address键。“key”都是字符串,和“value”可以是任何类型的对象[1]。在最基本的层面上,KVC只有两个方法:一个方法来改变这个值对于一个给定的键(mutator),和一个方法来检索值对于一个给定的键(访问)。这里有一个例子:
voidChangeName(Person*p,NSString*newName)
{
//using the KVC accessor (getter) method
NSString*originalName=[pvalueForKey:@"name"];
//using the KVC mutator (setter) method.
[psetValue:newNameforKey:@"name"];
NSLog(@"Changed %@'s name to: %@",originalName,newName);
}
现在让我们来看 Person对象有第三个key:一个 spouse键。 spouse键是另一个 Person对象的值。KVC可以让你进行这样的操作:
voidLogMarriage(Person*p)
{
//just using the accessor again, same as example above
NSString*personsName=[pvalueForKey:@"name"];
//this line is different, because it is using
//a "key path" instead of a normal "key"
NSString*spousesName=[pvalueForKeyPath:@"spouse.name"];
NSLog(@"%@ is happily married to %@",personsName,spousesName);
}
Cocoa在“key”和“KeyPath”有所区分。“key”允许您获取一个值在一个对象。一个“KeyPath”允许您链接多个键,用点隔开。例如,这个……
[pvalueForKeyPath:@"spouse.name"];
…是完全相同的,因为这…
[[pvalueForKey:@"spouse"]valueForKey:@"name"];
所有你需要知道的知识。
让我们继续KVO。
键-值观察(KVO)
键-值观察(KVO)是建立在KVC上的。在对象值改变时,它允许您观察(观察)一个KVC key path对象值。例如,让我们写一些代码,手表,看看一个人的地址更改。有 三种方法对下面的代码:
watchPersonForChangeOfAddress:开始观察
observeValueForKeyPath:ofObject:change:context:(被用为:Key Path的特定对象的value每次观被观察改变??此处不明确具体称呼方式~~~~~~~)
dealloc停止观察
static NSString*constKVO_CONTEXT_ADDRESS_CHANGED=@"KVO_CONTEXT_ADDRESS_CHANGED"
@implementationPersonWatcher
-(void)watchPersonForChangeOfAddress:(Person*)p;
{
//this begins the observing
[paddObserver:selfforKeyPath:@"address"options:0context:KVO_CONTEXT_ADDRESS_CHANGED];
//keep a record of all the people being observed,
//because we need to stop observing them in dealloc
[m_observedPeopleaddObject:p];}
//whenever an observed key path changes, this method will be called
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context;
{
//use the context to make sure this is a change in the address,
//because we may also be observing other things
if(context==KVO_CONTEXT_ADDRESS_CHANGED)
{
NSString*name=[objectvalueForKey:@"name"];
NSString*address=[objectvalueForKey:@"address"];
NSLog(@"%@ has a new address: %@",name,address);
}
}
-(void)dealloc;
{
//must stop observing everything before this object is
//deallocated, otherwise it will cause crashes
for(Person*pinm_observedPeople)
{
[premoveObserver:selfforKeyPath:@"address"];
}
[m_observedPeoplerelease];
m_observedPeople=nil;[superdealloc];
}
-(id)init;
{
if(self=[superinit])
{
m_observedPeople=[NSMutableArraynew];
}
returnself;
}
@end
这是KVO所有。它允许你去观察一个key path在一个对象的值发生变化时得到通知。
Cocoa Bindings允许你 synchronise 两个key paths [2]所以他们拥有相同的key 。当一个key paths更新,另一个也是如此。
例如,让我们假设你有一个 Person对象和一个 NSTextField,编辑人的地址。我们知道,每一个 Person对象有一个 address键,多亏了Cocoa Bindings Reference,我们也知道,每一个 NSTextField对象有 value键使用绑定。我们想要的是这两个key paths是同步的(即绑定)。这意味着,如果用户类型 NSTextField,它会自动更新地址 Person对象。另外,如果我们以编程方式更改的地址 Person对象,我们希望它会自动出现在 NSTextField。这可以这样实现:
voidBindTextFieldToPersonsAddress(NSTextField*tf,Person*p)
{
//This synchronises/binds these two together:
//The `value` key on the object `tf`
//The `address` key on the object `p`
[tfbind:@"value"toObject:pwithKeyPath:@"address"options:nil];
}
下面会发生什么是 NSTextField开始观察 address键在 Person通过KVO对象。如果地址的变化, Person对象,---NSTextField得到通知的改变,它将更新的新值。在这种情况下, NSTextField这类似于:
-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context;
{
if(context==KVO_CONTEXT_VALUE_BINDING_CHANGED)
{
[selfsetStringValue:[objectvalueForKeyPath:keyPath]];
}
}
当用户开始输入 NSTextField, NSTextField使用现有的更新 Person对象。在这种情况下, NSTextField这类似于:
-(void)insertText:(id)aString;
{
NSString*newValue=[[selfstringValue]stringByAppendingString:aString];
[selfsetStringValue:newValue];
//if "value" is bound, then propagate the change to the bound object
if([selfinfoForBinding:@"value"])
{
idboundObj=...;//omitted for brevity
NSString*boundKeyPath=...;//omitted for brevity
[boundObjsetValue:newValueforKeyPath:boundKeyPath];
}
}
结论
这是KVC、 KVO和绑定工作的基本知识,。视图使用现有的更新模型,并使用KVO注意模型。我有了不少的细节为了保持文章简短,但是希望它给了你一个公司掌握的概念和原则。