iOS-KVO(一) 基本操作
iOS-KVO(二) 使用注意點(diǎn)
iOS-KVO(三) 窺探底層實(shí)現(xiàn)
iOS-KVO(四) 自定義KVO+Block
這一節(jié),將自定義KVO,并且通過Block進(jìn)行返回屬性的變化。只是驗(yàn)證大致流程,并未做一些判斷,比如傳入key是空之類的。
我們還是按照以下流程來講代碼:
KVO主要流程:
- 創(chuàng)建一個(gè)中間類并創(chuàng)建class方法,返回父類;
- isa指針指向中間類;
- 重寫setter方法;
另外自己另外寫了一個(gè)類(LHObserver),來存放觀察者,觀察的key,以及block。
以下的代碼全部都在一個(gè)類別中實(shí)現(xiàn):
NSObject+BlockKVO
先簡(jiǎn)單看下 LHObserver這個(gè)類
@interface LHObserver : NSObject
@property (nonatomic, weak) NSObject *observer;
@property (nonatomic, copy) NSString *observerKey;
@property (nonatomic, weak) id context;
@property (nonatomic, copy) LH_ObserverHandler observerHandler;
@end
@implementation LHObserver
- (instancetype)initWithObersver:(NSObject *)observer
observerKey:(NSString *)observerKey
observerHandler:(LH_ObserverHandler)observerHandler
context:(nullable id)context
{
self = [super init];
if ( self ) {
self.observer = observer;
self.observerKey = observerKey;
self.observerHandler = observerHandler;
self.context = context;
}
return self;
}
@end
其中LH_ObserverHandler這個(gè)block為:
/**
監(jiān)聽屬性變化
@param observerObject 觀察者
@param observerKey 觀察的名稱
@param object 被觀察者
@param change 監(jiān)聽返回的信息
@param context 上下文
*/
typedef void(^LH_ObserverHandler)(id observerObject, NSString *observerKey, id object, NSDictionary<NSKeyValueChangeKey,id> *change, id context);
接下來開始KVO的流程代碼
創(chuàng)建一個(gè)中間類
- 拼接setter方法
/**
獲取setter方法
@param observerKey 觀察的名稱
@return setter SEL
*/
- (SEL)setterSELWithObserverKey:(NSString *)observerKey
{
//比如observerKey的值為name,那么setter的方法名稱就是 setName: 冒號(hào)不要忘記咯~
return NSSelectorFromString([NSString stringWithFormat:@"set%@:", [observerKey capitalizedString]]);
}
- 創(chuàng)建中間類
/**
創(chuàng)建中間類
@param originalClass 原先的類
@return 中間類
*/
- (Class)createKVOClassWithOriginalClass:(Class)originalClass
{
//獲取原先類的名稱
NSString *originalClassName = [NSString stringWithUTF8String:object_getClassName(originalClass)];
//拼接中間類的名稱
NSString *kvoClassName = [NSString stringWithFormat:@"%@%@", KVO_CLASS_NAME_PREFIX, originalClassName];
//判斷中間類是否存在,如果存在直接返回即可
Class kvoClass = objc_getClass(kvoClassName.UTF8String);
if ( kvoClass ) {
return kvoClass;
}
//不存在的話,就開始初始化與注冊(cè)類
kvoClass = objc_allocateClassPair([self class], kvoClassName.UTF8String, 0);
objc_registerClassPair(kvoClass);
//添加class的方法,返回父類
Method classMethod = class_getInstanceMethod(originalClass, @selector(class));
class_addMethod(kvoClass, NSSelectorFromString(@"class"), (IMP)getKVOClass, method_getTypeEncoding(classMethod));
NSLog(@"%d", [self hasSelectorWithClass:kvoClass sel:@selector(class)]);
return kvoClass;
}
/**
返回父類
@param self self
@param _cmd _cmd
@return 父類
*/
Class getKVOClass(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}
isa指針指向中間類
//1.創(chuàng)建一個(gè)中間類, 2.并且isa指針指向中間類;
Class kvoClass = [self createKVOClassWithOriginalClass:[self class]];
//2.isa指針指向中間類;
object_setClass(self, kvoClass);
重寫setter方法
- 先判斷當(dāng)前類是否已經(jīng)存在setter方法了,如果有的話就不在添加
/**
是否已經(jīng)存在sel方法
@param cls 類
@param sel sel方法
@return YES:存在 NO:不存在
*/
-(BOOL)hasSelectorWithClass:(Class)cls sel:(SEL)sel
{
unsigned int outCount = 0;
Method *methodList = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; ++i) {
Method method = methodList[i];
SEL selector = method_getName(method);
if ( selector == sel ) {
free(methodList);
return YES;
}
}
free(methodList);
return NO;
}
- setter方法的實(shí)現(xiàn)
//3.重寫setter方法;
//(1)獲取setter方法 - 上面已經(jīng)先獲取了
//(2)判斷中間類是否存在setter方法
if ( ![self hasSelectorWithClass:kvoClass sel:setter] ) {
//(3)不存在就添加setter方法
class_addMethod(kvoClass, setter, (IMP)setterIMP, method_getTypeEncoding(setterMethod));
}
//存儲(chǔ)LHObserver的對(duì)象的可變數(shù)組
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers ) {
observers = [NSMutableArray array];
}
//初始化LHObserver的實(shí)例對(duì)象
LHObserver *observerInfo = [[LHObserver alloc] initWithObersver:observer
observerKey:observerKey
observerHandler:observerHandler
context:context];
[observers addObject:observerInfo];
objc_setAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
void setterIMP(id self, SEL _cmd, id newValue)
{
/*
1.willChangeValueForKey
2.[super setter]
3.didChangeValueForKey
*/
//獲取getter方法名稱
NSString *getterFuncName = objc_getAssociatedObject(self, &LH_GETTER_FUNCTION_NAME);
//先將當(dāng)前中間類保存下來
Class kvoClass = object_getClass(self);
//先將isa指針指向父類,注意之前已經(jīng)實(shí)現(xiàn)過class方法
Class cls = [self class];
object_setClass(self, cls);
//獲取舊數(shù)據(jù)
id oldValue = ((id(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(getterFuncName));
//調(diào)用父類的setter
((id(*)(id, SEL, id))objc_msgSend)(self, _cmd, newValue);
//拼湊數(shù)據(jù)
NSMutableDictionary *change = [@{} mutableCopy];
//這里要根據(jù)自己的需求來定,如果對(duì)應(yīng)的value是空的話,直接賦值空字符串
if ( oldValue ) {
change[NSKeyValueChangeOldKey] = oldValue;
} else {
change[NSKeyValueChangeOldKey] = @"";
}
if ( newValue ) {
change[NSKeyValueChangeNewKey] = newValue;
} else {
change[NSKeyValueChangeNewKey] = @"";
}
//再將isa指針指向中間類
object_setClass(self, kvoClass);
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers || observers.count <= 0 ) {
return;
}
//循環(huán)獲取observerObject,然后執(zhí)行block
for (LHObserver *observerObject in observers) {
if ( [observerObject.observerKey isEqualToString:getterFuncName] ) {
if ( observerObject.observerHandler ) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//這里是在子線程
observerObject.observerHandler(observerObject.observer, observerObject.observerKey, self, change, observerObject.context);
});
}
break;
}
}
}
移除觀察者
/**
移除觀察者
@param observer 觀察者
@param observerKey 觀察的名稱
*/
- (void)lh_removeObserver:(NSObject *)observer forObserverKey:(NSString *)observerKey
{
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers ) {
return;
}
LHObserver *tempObserverObject = nil;
for (LHObserver *observerObject in observers) {
if ( observerObject.observer == observer && [observerObject.observerKey isEqualToString:observerKey] ) {
tempObserverObject = observerObject;
break;
}
}
//移除
if ( tempObserverObject ) {
[observers removeObject:tempObserverObject];
}
}
簡(jiǎn)單使用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "ViewController.h"
#import "Person.h"
#import "NSObject+BlockKVO.h"
@interface ViewController ()
@property (nonatomic, strong) Person *p1, *p2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.p1 = [Person new];
[self.p1 setName:@"1"];
[self.p1 lh_addObserver:self
forObserverKey:@"name"
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
context:@"123"
observerHandler:^(id _Nonnull observerObject, NSString * _Nonnull observerKey, id _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change, id _Nonnull context) {
NSLog(@"p1 %@, context:%@", change, context);
}];
[self.p1 setName:@"3"];
[self.p1 lh_removeObserver:self forObserverKey:@"name"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.p1 setName:@"hui"];
}
- (void)dealloc
{
NSLog(@"%@ dealloc", [self class]);
}
@end
打印結(jié)果:
2019-07-05 23:52:53.765 KVODemo[5158:175811] {
new = 3;
old = 1;
}, context:123
這章主要都是代碼,將之前說的底層原理的流程,自己通過代碼簡(jiǎn)單的實(shí)現(xiàn),代碼還有很多可以改進(jìn)的地方。
上面代碼 傳送門
KVO over!