1 首先根据key 生成Set方法
SEL setter = NSSelectorFromString(setterForKeyPath(keyPath));
static NSString* setterForKeyPath(NSString*keyPath){
if (!keyPath.length) return nil;
NSString *firstLetter = [keyPath substringToIndex:1].uppercaseString;
NSString *secondLetter =[keyPath substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@",firstLetter,secondLetter];
}
2 检测observer 检测set方法 是否存在 不存在抛出异常
Method setterMethos = class_getInstanceMethod([observer class], setter);
if (!setterMethos) {
NSString *reason =[NSString stringWithFormat:@"Object %@ does not have a setter for key%@",observer,keyPath];
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
return;
}
3 根绝当前判断有没有生成过带前缀的子类对象 如果没有生成子类对象 注册子类
Class clazz = object_getClass(self);
NSString *className = NSStringFromClass(clazz);
if (![className hasPrefix:@"LLKVOPreFix"]) {
object_setClass(self, [self makeKvoClassWithOriginalClassName:className]);
}
-(Class)makeKvoClassWithOriginalClassName:(NSString*)originalClazzName{
NSString *kvoClassName =[@"LLKVOPreFix" stringByAppendingString:originalClazzName];
Class class = NSClassFromString(kvoClassName);
if (!class) {
return class;
}
Class orginalClazz = object_getClass(self);
Class kvoClazz = objc_allocateClassPair(orginalClazz, kvoClassName.UTF8String, 0);
Method classMethod = class_getInstanceMethod(orginalClazz, @selector(class));
const char *type = method_getTypeEncoding(classMethod);
class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, type);
objc_registerClassPair(kvoClazz);
return kvoClazz;
}
static Class kvo_class(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}
5 重写set方法
if (![self hasSelector:setter]) {
const char *types = method_getTypeEncoding(setterMethos);
class_addMethod(class, setter, (IMP)kvo_setter, types);
}
static void kvo_setter(id self,SEL _cmd,id newValue){
NSString *setterName = NSStringFromSelector(_cmd);
NSString *getterName = getterForSetterName(setterName);
id oldValue = [self valueForKey:getterName];
struct objc_super superclass = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
void(*objc_msgSendSuperCaster)(void*,SEL,id) = (void *)objc_msgSendSuper;
objc_msgSendSuperCaster(&superclass,_cmd,newValue);
}
static NSString *getterForSetterName(NSString * setterName){
if (setterName.length<=0||![setterName hasPrefix:@"set"]||![setterName hasSuffix:@":"]) {
return nil;
}
NSRange range = NSMakeRange(3, setterName.length-4);
NSString *key = [setterName substringWithRange:range];
NSString *fitst =[key substringToIndex:1].lowercaseString;
key =[key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:fitst];
return key;
}
-(BOOL)hasSelector:(SEL)selctor{
Class clazz = object_getClass(self);
unsigned int count = 0;
Method *methodList = class_copyMethodList(clazz, &count);
for (int i= 0; i<count; i++) {
SEL thisSelector =method_getName(methodList[i]);
if (thisSelector == selctor) {
free(methodList);
return YES;
}
}
free(methodList);
return NO;
}