说明注释全在代码里,还有RunTime的方法说明,如果我表达的有歧义,请看API英文说明
// NSObject+KVO.h
#import <Foundation/Foundation.h>
typedef void(^ObservingBlock) (id observedObject,NSString *observedkey,id oldValue,id newValue);
@interface NSObject (KVO)
///自定义添加观察者
- (void)addCustomObserver:(NSObject *)observer
key:(NSString *)key
block:(ObservingBlock)block;
///移除自定义观察者
- (void)removeCustomObserver:(NSObject *)observer forKey:(NSString *)key;
@end
//NSObject+KVO.m 里的全部代码,如下
#import "NSObject+KVO.h"
#include <objc/runtime.h>
#include <objc/message.h>
NSString *const KVO_CustomPrefix = @"KVO_CustomPrefix_";
NSString *const KVO_CustomObserverKey = @"KVO_CustomObserverKey";
///设置观察者配置信息
@interface ObserverInfo : NSObject
@property (nonatomic,strong) id observer;
@property (nonatomic,copy) NSString *key;
@property (nonatomic,copy) ObservingBlock block;
@end
@implementation ObserverInfo
- (instancetype)initWithObserver:(NSObject *)observer key:(NSString *)key block:(ObservingBlock)block
{
self = [super init];
if (self) {
_observer = observer;
_key = key;
_block = block;
}
return self;
}
@end
///setter 转getter setName:->name
static NSString *getterFromSetter(NSString *setter)
{
if (setter.length <= 0 && ![setter hasPrefix:@"set"] && ![setter hasSuffix:@":"]) {
return nil;
}
NSString *pNameStr = [setter substringWithRange:NSMakeRange(3, setter.length-3-1)];
NSString *first = [[pNameStr substringToIndex:1] lowercaseString];
return [pNameStr stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:first];
}
///setter 转getter name->setName:
static NSString *setterFromGetter(NSString *getter)
{
NSString *first = [[getter substringToIndex:1] uppercaseString];
NSString *upString = [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:first];
return [NSString stringWithFormat:@"set%@:",upString];
}
static void kvo_setter(id self,SEL _cmd,id newValue){
NSString *setterName = NSStringFromSelector(_cmd);
NSString *getterName = getterFromSetter(setterName);
if (!getterName) {
NSString *reason = [NSString stringWithFormat:@"Object %@ does not have setter %@", self, setterName];
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
return;
}
id oldValue = [self valueForKey:getterName];
struct objc_super superclass = {
.receiver = self,
.super_class= class_getSuperclass(object_getClass(self))
};
/*These functions must be cast to an appropriate function pointer type
* before being called
*/
void (*objc_msgSendSuperCasted)(void *,SEL, id) = (void *)objc_msgSendSuper;
objc_msgSendSuperCasted(&superclass,_cmd,newValue);
NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(KVO_CustomObserverKey));
for (ObserverInfo *each in observers) {
if ([each.key isEqualToString:getterName]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
each.block(self, getterName, oldValue, newValue);
});
}
}
}
static Class kvo_superClass(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}
@implementation NSObject (KVO)
- (void)addCustomObserver:(NSObject *)observer
key:(NSString *)key
block:(ObservingBlock)block
{
///伪代码MethodList:[Method:{SEL:IMP},Method:{SEL:IMP}....]
SEL setter = NSSelectorFromString(setterFromGetter(key));
// Returns a specified instance method for a given class.
Method setterMethod = class_getInstanceMethod([self class], setter);
///先判断是否有Setter Method
if (!setterMethod) {
NSString *reason = [NSString stringWithFormat:@"Object %@ does not have a setter for key %@", self, key];
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
return;
}
Class class = object_getClass(self);
NSString *className = NSStringFromClass(class);
///判断类是否已经添加过观察者,判断方式就是前缀
if (![className hasPrefix:KVO_CustomPrefix]) {
///获取新注册的类
class = [self makeDeriveKVOClassWithOriginClassName:className];
///把对象的class指针指向新注册的类
object_setClass(self, class);
}
///如果本类没有Setter SEL实现--IMP
if (![self hasSelector:setter]) {
//Returns a string describing a method's parameter and return types.
const char *types = method_getTypeEncoding(setterMethod);
//Adds a new method to a class with a given name and implementation.
class_addMethod(class, setter, (IMP)kvo_setter, types);
}
///初始化观察者
ObserverInfo *info = [[ObserverInfo alloc]initWithObserver:observer key:key block:block];
///获取属性变量observers
NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void*)(KVO_CustomObserverKey));
if (!observers) {
///如果属性变量不存在 就重新初始化,并添加到类属性列表里
observers = [NSMutableArray array];
objc_setAssociatedObject(self, (__bridge const void *)(KVO_CustomObserverKey), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
///把初始化的观察者信息对象添加到观察者里
[observers addObject:info];
}
- (void)removeCustomObserver:(NSObject *)observer forKey:(NSString *)key;
{
///获取属性变量Observers
NSMutableArray *observes = objc_getAssociatedObject(self, (__bridge const void*)(KVO_CustomObserverKey));
ObserverInfo *removeObserver;
///遍历观察者列表,找到一致的对象,移除
for (ObserverInfo *each in observes) {
if (each.observer == observer && [each.key isEqualToString:key]) {
removeObserver = each;
break;
}
}
[observes removeObject:removeObserver];
}
- (Class)makeDeriveKVOClassWithOriginClassName:(NSString *)originClassName
{
///根据原类名拼接出派生类
NSString *deriveClassName = [KVO_CustomPrefix stringByAppendingString:originClassName];
Class deriveClass = NSClassFromString(deriveClassName);
if (deriveClass) {
return deriveClass;
}
///获取本类
Class originClass = object_getClass(self);
/*Creates a new class and metaclass.
* @param superclass
* @param name
* @param extraBytes
*/
Class newClass = objc_allocateClassPair(originClass, deriveClassName.UTF8String, 0);
///为派生类的class方法添加实现
Method method = class_getInstanceMethod(originClass, @selector(class));
const char *types = method_getTypeEncoding(method);
class_addMethod(newClass, @selector(class), (IMP)kvo_superClass, types);
///注册到RunTime
objc_registerClassPair(newClass);
return newClass;
}
///判断是否有这个方法
- (BOOL)hasSelector:(SEL)selector
{
Class clazz = object_getClass(self);
unsigned int methodCount = 0;
Method* methodList = class_copyMethodList(clazz, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
SEL thisSelector = method_getName(methodList[i]);
if (thisSelector == selector) {
free(methodList);
return YES;
}
}
free(methodList);
return NO;
}
@end