ueForKeyPath是抛出异常。这是一个好的编程实践,因为它可以使错误更早的暴露出来。最好不要提供一个空的实现,这样可能由于编程者的错误而没有实现observeva lueForKeyPath,但是编译器并不会将这种错误表现出来。
3, 常用的类框架图
?
?
4, 在哪里添加和删除观察者?
添加和删除观察者的操作最好能够封装到目标或者观察者类体内,而不要如demo例子中所使用的使用一个driver进行操作(我主要意图是希望使类结构清晰点)。一般情况下可在init中加入,在dealloc中删除。这样使用者不会因为编程的疏忽导致忘记最后的删除操作。
七,KVO的实现
例如demo工程KVOPrinciple
KVO是通过叫isa-swizzling技术实现的,基本的流程就是编译器自动为被观察对象创造一个派生类,并将被观察对象的isa指向这个派生类。如果用户注册了对某此目标对象的某一个属性的观察,那么此派生类会重写这个方法,并在其中添加进行通知的代码。这个需要OC运行时候的流程有一个基本了解。OC在发送消息的时候,会通过isa指针找到当前对象所属的类对象,而类对象中保存着当前对象的实例方法,因此在向此对象发送消息时候,实际上是发送到了派生类对象的方法,又由于编译器对派生类的方法进行了override,并添加了通知代码,因此会向注册的对象发送通知。注意派生类只重写注册了观察者的属性方法。
?
举例:
有一个Person类,它有三个属性firstName, lastName, personId,它是被观察对象。有一个PersonObserver类,它观察firstName和lastName,不观察personId。
理论来说,派生类会覆盖基类的setFirstName和setLastName方法,不覆盖setPersonId方法。通过运行时函数object_getClass(p)访问此person对象,会得到一个名字为NSKVONotifying_Person的类对象,如果直接通过[p class]方法访问,那么会看到它依然为person对象,这是因为派生类同样重写了class函数,当向p发送class消息时候,同样是找到了派生类的class方法。
通过分别打印不同类对象的函数表进行测试。对于派生类和基类取得其setFirstName的IMP实现,然后进行直接调用比较。现在对于基类使用IMP访问会崩溃,暂未知原因。对于派生类使用其IMP实现,确实可以正常发送通知,这表明确实是使用的派生类的实现。
八,总结
1,简介
键值编码是使用String标识符间接获取对象属性的机制。它是Cocoa编程的基础,包括Cocoa Data,应用脚本化,绑定技术,属性声明。脚本化和绑定技术是专门为OSX的Cocoa而用,你可以使用键值编码去简化编程代码,Jastor就使用统一的setValueForKey,以及valueForKey。
2,访问对象属性和KVC
2.1,KVC常用以下四个方法:
-(id)valueForKey:(NSString *)key;
-(void)setValue:(id)value forKey:(NSString *)key; - -(id)valueForKeyPath:(NSString*)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
KVC通过字符串的key或者keypath定位对象的属性。Key有命名管理,以ASCII编码,小写字母开头,无空格,它一般就是属性的名字。KeyPath是一个用点分割的字符串用来逐层遍历最深层的属性值。
?
如上例子:
[employee1setValue:@”lpstudy” forKey:@”name”];
[employee1setValue:@[employee1, employee2, employee3]forKeyPath:@”manager.directerPorts”];
2.2,使用访问器方法
如果有,访问器方法依然是有效的。如果没有相应的访问器方法,可以使用上面的setValue和valueForKey直接读取对象的属性值。
2.3,具有KVC能力
如何使一个类具有KVC功能,必须满足下列的其中一个:
l 类使用key声明了属性
@property (nonatomic ,readonly,strong) NSString *personName;
l 它实现了key名字的get方法和set方法。如果返回bool类型,isKey方法。
l 声明了一个实例变量,key或者_key
需要注意
l valueForKey和valueForKeyPath 返回指定key,keyPath的value值。如果不存在此key,会调用valueForUndefinedKey:。它的默认实现是抛出NSUndefinedKeyException;子类可以复写这种行为。
l 尝试给一个非对象类型设置nil。对象默认调用setNilValueForKey此方法默认抛出NSInvalidArgumentException.你的程序可以覆盖这个方法给一个默认值,然后 setValue:forKey: 设置新值。
l 鉴权机制validatePersonName
2.4,搜索方式
以下选自于网络,我并没有进行相关的测试。
setValue:forKey的搜索方式:
1,首先搜索set
:方法。如果成员用@property,@synthsize处理,因为@synthsize告诉编译器自动生成set
:格式的setter方法,所以这种情况下会直接搜索到。
2,上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly返回YES(注:这是NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES),则按_
,_is
,
,is
的顺序搜索成员的名值。
3,如果找到则设置成员的值,没有找到则调用setValue:forUndefinedKey:。
valueForKey的搜索方式
1.,按get
、
、is
的顺序查找getter方法,找到直接调用。如果是bool、int等内建值类型,会做NSNumber的转换,如果是其他类似于struct结构体类型则会进行NSValue的转换。
2,否则查找countOf
、objectIn
AtIndex:、
AtIndexes格式的方法。 如果countOf
和另外两个方法中的一个找到,那么就会返回一个可以响应NSArray所有方法的代理集合(collection proxy object)。发送给这个代理集合(collection proxy object)的NSArray消息方法,就会以countOf
、objectIn
AtIndex:、
AtIndexes这几个方法组合的形式调用。还有一个可选的get
:range:方法。
3,还没找到则继续查找countOf
、enumeratorOf
、memberOf
:格式的方法。 如果这三个方法都找到,那么就