自定义KVO

封装成闭包的形式更方便使用,也避免了移除kvo以及回调的处理
代码来自github

//
//  NSObject+RCKVO.h
//  CustomKVO
//
//  Created by 孙承秀 on 2018/5/23.
//  Copyright © 2018年 孙承秀. All rights reserved.
//

#import <Foundation/Foundation.h>
typedef void (^RCObserverBlock)(id observerObject , id key , id oldValue , id newValue);
@interface NSObject (RCKVO)
- (void)rc_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(RCObserverBlock)block;
@end
//
//  NSObject+RCKVO.m
//  CustomKVO
//
//  Created by 孙承秀 on 2018/5/23.
//  Copyright © 2018年 孙承秀. All rights reserved.
//

#import "NSObject+RCKVO.h"
#import <objc/message.h>

@interface RCObserverInfo:NSObject
/**
 observer
 */
@property(nonatomic , weak)NSObject *observer;
/**
 key
 */
@property(nonatomic , strong)NSString *key;
/**
 block
 */
@property(nonatomic , strong)RCObserverBlock block;
@end

@implementation RCObserverInfo
- (instancetype)initWithObserver:(NSObject *)obserser forKey:(NSString *)key block:(RCObserverBlock)block{
    if (self = [super init]) {
        _observer = obserser;
        _key = key;
        _block = block;
    }
    return self;
}
@end

NSString *const prefixName = @"RCKVONotifiying_";
NSString *const observersKey = @"RCObservers";

#pragma mark -------------- tool ---------------

/**
 设置setter方法名字

 @param setterName 方法名字不带set
 @return 带有set的方法名字
 */
static NSString *getSetterName(NSString *setterName){
    if (setterName.length <= 0) {
        NSLog(@"setter name must is not nil");
        return nil;
    }
    NSString *first = [[setterName substringToIndex:1] uppercaseString];
    NSString *second = [setterName substringFromIndex:1];
    NSString *setterMethodName = [ NSString stringWithFormat:@"set%@%@:",first,second];
    return setterMethodName;
}

static Class kvo_class(id self,SEL _cmd){
    return class_getSuperclass(objc_getClass((__bridge void *)self));
}

/**
 获取原始的key名字

 @param setter set 方法的名字
 @return 原key值
 */
static NSString *Key_name(NSString *setter){
    if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"] ) {
        NSLog(@"setter is inviable");
        return nil;
    }
    NSRange keyRange = NSMakeRange(3, setter.length - 4);
    NSString *key = [setter substringWithRange:keyRange];
    NSString *first = [[key substringToIndex:1] lowercaseString];
    NSString *oriKey = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:first];
    return oriKey;
}

/**
 触发原始setter方法,并通过block返回新的监听值
 */
static void KVO_FireSetter(id self,SEL _cmd , id newValue){
    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *key = Key_name(setterName);
    if (!key) {
        NSLog(@"key is nil");
        return;
    }
    struct objc_super supperClass = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };
    void (*objc_msgSendFireSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
    id oldValue = [self valueForKey:key];
    objc_msgSendFireSuper(&supperClass,_cmd,newValue);
    NSMutableArray *observers = objc_getAssociatedObject(self, &observersKey);
    for (RCObserverInfo *info in observers) {
        if ([info.key isEqualToString:key]) {
            if (info.block) {
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    info.block(self, key, oldValue, newValue);
                });
                break;
            }
        }
    }
    
}
@implementation NSObject (RCKVO)
-(void)rc_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(RCObserverBlock)block{
    NSString *setterName = getSetterName(key);
    SEL setter = NSSelectorFromString(setterName);
    Method setterMethod = class_getInstanceMethod([self class], setter);
    if (setterMethod == nil) {
        NSString *des = [NSString stringWithFormat:@"method %@ is nil",setterMethod];
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:des userInfo:nil];
        return;
    }
    RCObserverInfo *info = [[RCObserverInfo alloc] initWithObserver:observer forKey:key block:block];
    NSMutableArray *observers = objc_getAssociatedObject(self, &observersKey);
    if (!observers) {
        observers = [NSMutableArray array];
        objc_setAssociatedObject(self, &observersKey, observers,  OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    [observers addObject:info];
    Class selfClass = object_getClass(self);
    Class newClass ;
    NSString *selfClassName = NSStringFromClass(selfClass);
    if (![selfClassName hasPrefix:prefixName]) {
        newClass = [self createKVONotifiyingClass:selfClassName];
        object_setClass(self, newClass);
    }
    if (![self hasSelector:setter]) {
        const char* types =  method_getTypeEncoding(setterMethod);
        class_addMethod(newClass, setter, (IMP)KVO_FireSetter, types);
    }
}
- (Class)createKVONotifiyingClass:(NSString *)className{
    NSString *kvoName =[prefixName stringByAppendingString:className];
    Class kvoClass = NSClassFromString(kvoName);
    if (kvoClass) {
        return kvoClass;
    }
    Class oriClass = NSClassFromString(className);
    Method method = class_getInstanceMethod(oriClass, @selector(class));
    
    Class kvoNotifiyingClass = objc_allocateClassPair(oriClass, kvoName.UTF8String, 0);
    const char * type =method_getTypeEncoding(method);
    class_addMethod(kvoNotifiyingClass, @selector(class), (IMP)kvo_class, type);
    objc_registerClassPair(kvoNotifiyingClass);
    return kvoNotifiyingClass;
    
}
- (BOOL)hasSelector:(SEL)selector{
    unsigned int count;
    Class selfClass = objc_getClass((__bridge void *)self);
    Method *methodList = class_copyMethodList(selfClass, &count);
    for (NSInteger i = 0; i < count ; i ++) {
        SEL _sel = method_getName(methodList[i]);
        if (_sel == selector) {
            free(methodList);
            return YES;
        }
    }
    free(methodList);
    return NO;
}
@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值