手记

iOS学习笔记--KVO的代码实现

说明注释全在代码里,还有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
0人推荐
随时随地看视频
慕课网APP