KVO下地実装原理
2392 ワード
原理:サブクラスを動的に生成し、setメソッドを書き換え、observeValueForKeyPathメッセージを配布する
KVOで使用されるruntimeインタフェースのカスタマイズ
カスタムKVO例
KVOで使用されるruntimeインタフェースのカスタマイズ
// root
objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
//
void objc_registerClassPair(Class cls)
//
BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
//
Class object_setClass(id obj, Class cls)
//
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
カスタムKVO例
#import "NSObject+WHJKVO.h"
#import
@implementation NSObject (WHJKVO)
- (void)whj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
//
NSString * superClass = NSStringFromClass([self class]);
NSString *subClassName = [NSString stringWithFormat:@"whj_%@",superClass];
const char *name = [subClassName UTF8String];
Class myClass = objc_allocateClassPair([self class], name, 0);
//
objc_registerClassPair(myClass);
//
NSString *methodName = [NSString stringWithFormat:@"set%@:",[keyPath capitalizedString]];
objc_setAssociatedObject(self, @"methodName", methodName, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //Person
class_addMethod(myClass, NSSelectorFromString(methodName), (IMP)myImp, "V@:@");
//
object_setClass(self, myClass);// self, whj_Person
// , observer, observer
objc_setAssociatedObject(self, @"objc", observer, OBJC_ASSOCIATION_ASSIGN); //whj_Person
}
void myImp(id self, SEL _cmd,id param) {
// super
//
id class = [self class];
//
object_setClass(self,class_getSuperclass(class));
NSString *methodName = objc_getAssociatedObject(self, @"methodName"); // Person
//
objc_msgSend(self,NSSelectorFromString(methodName),param);
//
id observer = objc_getAssociatedObject(self, @"objc"); // Person
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),param,self,nil,nil);
// , whj_Person myImp
object_setClass(self, class);
}