参考文章 http://www.cocoachina.com/ios/20150313/11321.html
本文在此基础上为每段代码添加详细的注释
#import <Foundation/Foundation.h>
typedef void (^ZZXObservingBlock) (id observedObject,NSString *observedKey, id oldValue, id newValue);
@interface NSObject (ZZXKVO)
/*
添加自己的 addobserver
取代NSObject原有的 [self addObserver:(nonnull NSObject *) forKeyPath:(nonnull NSString *) options:(NSKeyValueObservingOptions) context: (nullable void *)];
PGObservingBlock block回调
取代 NSObject原有的- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
*/
- (void)ZZX_addObserver:(NSObject *)observer forkey:(NSString *)key withBlock:(ZZXObservingBlock)block;
- (void)ZZX_removeObserver:(NSObject *)observer forKey:(NSString *)key;
@end
2.然后是具体实现部分
注意点
1. objc_msgSendSuperCasted(&superclazz, _cmd, newValue); 方法里面一定要给发消息给父类执行父类的setter
2. object_setClass(self, clazz); 方法里面一定要给发消息给父类执行父类的setter
#import "NSObject+ZZXKVO.h"
#import <objc/runtime.h>
#import <objc/message.h>
/*
新建类名的前缀
*/
NSString *const ZZXKVOClassPreFix =@"ZZXKVOClassPreFix_";
/*
动态添加数组的标识
*/
NSString *const ZZXKVOAssociatedObservers =@"ZZXKVOAssociatedObservers";
#pragma mark - ZZXObservationInfo
/*
添加的observer信息,存储在数组里
*/
@interface ZZXObservationInfo :NSObject
@property (nonatomic,weak) NSObject *observer;
@property (nonatomic,copy) NSString *key;
@property (nonatomic,copy) ZZXObservingBlock block;
@end
@implementation ZZXObservationInfo
- (instancetype)initWithObserver:(NSObject *)observer Key:(NSString *)key block:(ZZXObservingBlock)block
{
self = [superinit];
if (self) {
_observer = observer;
_key = key;
_block = block;
}
returnself;
}
@end
@implementation NSObject (ZZXKVO)
#pragma mark - Helpers
/*
通过setter的函数名,获取属性的名字,转换时要去掉“set”,":",并小写名字的第一个字母
*/
static NSString * getterForSetter(NSString *setter)
{
if (setter.length <=0 || ![setter hasPrefix:@"set"] || ![setterhasSuffix:@":"]) {
returnnil;
}
NSRange range =NSMakeRange(3, setter.length -4);
NSString *key = [settersubstringWithRange:range];
// lower case the first letter
NSString *firstLetter = [[keysubstringToIndex:1]lowercaseString];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:firstLetter];
return key;
}
/*
通过属性名,获取setter方法名,setter方法第一个字母大写
*/
static NSString * setterForGetter(NSString *getter)
{
if (getter.length <=0) {
returnnil;
}
// upper case the first letter
NSString *firstLetter = [[gettersubstringToIndex:1]uppercaseString];
NSString *remainingLetters = [gettersubstringFromIndex:1];
// add 'set' at the begining and ':' at the end
NSString *setter = [NSStringstringWithFormat:@"set%@%@:", firstLetter, remainingLetters];
return setter;
}
#pragma mark - Overridden Methods
/*
新建类自己的setter方法
*/
static void kvo_setter(idself, SEL_cmd, id newValue)
{
NSString *setterName =NSStringFromSelector(_cmd);
NSString *getterName =getterForSetter(setterName);
if (!getterName) {
NSString *reason = [NSStringstringWithFormat:@"Object %@ does not have setter %@",self, setterName];
@throw [NSExceptionexceptionWithName:NSInvalidArgumentException
reason:reason
userInfo:nil];
return;
}
id oldValue = [selfvalueForKey:getterName];
structobjc_super superclazz = {
.receiver =self,
.super_class =class_getSuperclass(object_getClass(self))
};
// cast our pointer so the compiler won't complain
void (*objc_msgSendSuperCasted)(void *,SEL, id) = (void *)objc_msgSendSuper;
// call super's setter, which is original class's setter method
//给父类发送setter,通过断点可以看到执行新执行的是新建类的setter然后是父类的setter
objc_msgSendSuperCasted(&superclazz, _cmd, newValue);
// look up observers and call the blocks
//添加数组用于保存各个添加进来的观察者
NSMutableArray *observers =objc_getAssociatedObject(self, (__bridgeconst void *)(ZZXKVOAssociatedObservers));
for (ZZXObservationInfo *eachin observers) {
if ([each.keyisEqualToString:getterName]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
each.block(self, getterName, oldValue, newValue);
});
}
}
}
static Class kvo_class(idself, SEL_cmd)
{
returnclass_getSuperclass(object_getClass(self));
}
- (void)ZZX_addObserver:(NSObject *)observer forkey:(NSString *)key withBlock:(ZZXObservingBlock)block
{
// Step 1: Throw exception if its class or superclasses doesn't implement the setter
SEL setterSelector =NSSelectorFromString(setterForGetter(key));
Method setterMethod =class_getInstanceMethod([selfclass], setterSelector);
//如果没有setter,比如属性是只读
if (!setterMethod) {
NSString *reason = [NSStringstringWithFormat:@"Object %@ does not have a setter for key %@",self, key];
@throw [NSExceptionexceptionWithName:NSInvalidArgumentException
reason:reason
userInfo:nil];
return;
}
Class clazz = object_getClass(self);
NSString *clazzName =NSStringFromClass(clazz);
// if not an KVO class yet
//第一次添加观察者时,还没有创建该类,第二次添加观察者时就不用再创建了
if (![clazzNamehasPrefix:ZZXKVOClassPreFix]) {
clazz = [selfmakeKvoClassWithOriginalClassName:clazzName];
//设置self类型为新建的子类,这样设置属性的值时就会调用新建的子类的setter
object_setClass(self, clazz);
}
// add our kvo setter if this class (not superclasses) doesn't implement the setter?
if (![selfhasSelector:setterSelector]) {
constchar *types = method_getTypeEncoding(setterMethod);
class_addMethod(clazz, setterSelector, (IMP)kvo_setter, types);
}
//开始回调value给各个观察者
ZZXObservationInfo *info = [[ZZXObservationInfoalloc] initWithObserver:observerKey:key block:block];
NSMutableArray *observers =objc_getAssociatedObject(self, (__bridgeconst void *)(ZZXKVOAssociatedObservers));
if (!observers) {
observers = [NSMutableArrayarray];
objc_setAssociatedObject(self, (__bridgeconst void *)(ZZXKVOAssociatedObservers), observers,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observers addObject:info];
}
- (void)ZZX_removeObserver:(NSObject *)observer forKey:(NSString *)key
{
NSMutableArray* observers =objc_getAssociatedObject(self, (__bridgeconst void *)(ZZXKVOAssociatedObservers));
ZZXObservationInfo *infoToRemove;
for (ZZXObservationInfo* infoin observers) {
if (info.observer == observer && [info.keyisEqual:key]) {
infoToRemove = info;
break;
}
}
[observers removeObject:infoToRemove];
}
- (Class)makeKvoClassWithOriginalClassName:(NSString *)originalClazzName
{
//originalClazzName父类名字 kvoClazzName子类名字
NSString *kvoClazzName = [ZZXKVOClassPreFixstringByAppendingString:originalClazzName];
Class clazz = NSClassFromString(kvoClazzName);
if (clazz) {
return clazz;
}
// class doesn't exist yet, make it
Class originalClazz = object_getClass(self);
//新建类
Class kvoClazz = objc_allocateClassPair(originalClazz, kvoClazzName.UTF8String,0);
// grab class method's signature so we can borrow it
Method clazzMethod =class_getInstanceMethod(originalClazz,@selector(class));
constchar *types = method_getTypeEncoding(clazzMethod);
class_addMethod(kvoClazz,@selector(class), (IMP)kvo_class, types);
//这个一定要在新建方法之后再执行,执行之后,类不能再添加方法了
objc_registerClassPair(kvoClazz);
return kvoClazz;
}
- (BOOL)hasSelector:(SEL)selector
{
Class clazz = object_getClass(self);
unsignedint methodCount = 0;
Method* methodList =class_copyMethodList(clazz, &methodCount);
for (unsignedint i = 0; i < methodCount; i++) {
SEL thisSelector =method_getName(methodList[i]);
if (thisSelector == selector) {
free(methodList);
returnYES;
}
}
free(methodList);
returnNO;
}
@end
#import <Foundation/Foundation.h>
#import "NSObject+ZZXKVO.h"
@interface zzxKvoTest : NSObject
@property (nonatomic,strong) NSString* name;
+ (id)getInstance;
@end
@implementation zzxKvoTest
+ (id)getInstance
{
staticzzxKvoTest *sharedManager;
staticdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[zzxKvoTestalloc] init];
});
return sharedManager;
}
- (void)setName:(NSString *)name
{
NSLog(@"hehe");
}
@end
[[zzxKvoTestgetInstance] ZZX_addObserver:selfforkey:@"name"withBlock:^(id observedObject,NSString *observedKey, id oldValue, id newValue) {
}];
完成,有不清楚的地方自己运行代码,断点跟踪,注意self的类型变化。