是否經(jīng)常在項(xiàng)目中遇到kvo移除崩潰的錯(cuò)誤?
其實(shí)我們可以用try catch來(lái)解決這個(gè)問(wèn)題。
今天看到了二種比較優(yōu)雅的解決辦法。
+ (void)load
{
[self switchMethod];
}
+ (void)switchMethod
{
SEL removeSel = @selector(removeObserver:forKeyPath:);
SEL myRemoveSel = @selector(removeSafe:forKeyPath:);
SEL addSel = @selector(addObserver:forKeyPath:options:context:);
SEL myaddSel = @selector(addSafe:forKeyPath:options:context:);
Method systemRemoveMethod = class_getClassMethod([self class],removeSel);
Method DasenRemoveMethod = class_getClassMethod([self class], myRemoveSel);
Method systemAddMethod = class_getClassMethod([self class],addSel);
Method DasenAddMethod = class_getClassMethod([self class], myaddSel);
method_exchangeImplementations(systemRemoveMethod, DasenRemoveMethod);
method_exchangeImplementations(systemAddMethod, DasenAddMethod);
}
// 交換后的方法
- (void)removeSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
if ([self observerKeyPath:keyPath observer:observer]) {
[self removeSafe:observer forKeyPath:keyPath];
}
}
// 交換后的方法
- (void)addSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
if (![self observerKeyPath:keyPath observer:observer]) {
[self addSafe:observer forKeyPath:keyPath options:options context:context];
}
}
// 進(jìn)行檢索獲取Key
- (BOOL)observerKeyPath:(NSString *)key observer:(id )observer
{
id info = self.observationInfo;
NSArray *array = [info valueForKey:@"_observances"];
for (id objc in array) {
id Properties = [objc valueForKeyPath:@"_property"];
id newObserver = [objc valueForKeyPath:@"_observer"];
NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
return YES;
}
}
return NO;
}
我們可以利用
id info = self.observationInfo;
NSArray *array = [info valueForKey:@"_observances"];
拿到當(dāng)前類進(jìn)行kvo監(jiān)聽(tīng)到對(duì)象拍柒。如下圖所示
然后通過(guò)對(duì)比監(jiān)聽(tīng)到key和監(jiān)聽(tīng)的對(duì)象是否相同。
id Properties = [objc valueForKeyPath:@"_property"];
id newObserver = [objc valueForKeyPath:@"_observer"];
還有一種解決方案屈暗。
@interface ObserverData : NSObject
@property (nonatomic, strong)id objc;
@property (nonatomic, copy) NSString *keyPath;
- (instancetype)initWithObjc:(id)objc key:(NSString *)key;
@end
@implementation ObserverData
- (instancetype)initWithObjc:(id)objc key:(NSString *)key
{
if (self = [super init]) {
self.objc = objc;
self.keyPath = key;
}
return self;
}
@end
#import "DSObserver.h"
@implementation DSObserver
+ (instancetype)sharedDSObserver
{
static id objc;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
objc = [NSMutableArray array];
});
return objc;
}
@end
#pragma mark - 第二種方案拆讯,利用私有屬性
// 交換后的方法
- (void)removeSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
NSMutableArray *Observers = [DSObserver sharedDSObserver];
ObserverData *userPathData = [self observerKeyPath:keyPath];
// 如果有該key值那么進(jìn)行刪除
if (userPathData) {
[Observers removeObject:userPathData];
[self removeSafe:observer forKeyPath:keyPath];
}
return;
}
// 交換后的方法
- (void)addSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
ObserverData *userPathData= [[ObserverData alloc]initWithObjc:self key:keyPath];
NSMutableArray *Observers = [DSObserver sharedDSObserver];
// 如果沒(méi)有注冊(cè),那么才進(jìn)行注冊(cè)
if (![self observerKeyPath:keyPath]) {
[Observers addObject:userPathData];
[self addSafe:observer forKeyPath:keyPath options:options context:context];
}
}
//
// 進(jìn)行檢索养叛,判斷是否已經(jīng)存儲(chǔ)了該Key值
- (ObserverData *)observerKeyPath:(NSString *)keyPath
{
NSMutableArray *Observers = [DSObserver sharedDSObserver];
for (ObserverData *data in Observers) {
if ([data.objc isEqual:self] && [data.keyPath isEqualToString:keyPath]) {
return data;
}
}
return nil;
}
可以新建一個(gè)全局的管理kvo監(jiān)聽(tīng)管理者數(shù)組种呐,然后如果監(jiān)聽(tīng)就add,移除就remove弃甥,進(jìn)行之前爽室,進(jìn)行判斷是否已經(jīng)存在或者是已經(jīng)移除,來(lái)避免問(wèn)題淆攻。
其實(shí)我比較推薦第二種解決方案阔墩。因?yàn)榈谝环N應(yīng)該會(huì)有一些特殊情況不能完全覆蓋到。