iOS开发之常用技术点iOS DeveloperiOS

iOS-设计一个在dealloc中自动移除KVO的分类

2019-01-26  本文已影响49人  耿杰

KVO在项目中使用很多,主要是两种原因会使KVO崩溃

设计思路

//
//  NSObject+KVO.m
//  CrashSafe
//
//  Created by 无头骑士 GJ on 2019/1/26.
//  Copyright © 2019 无头骑士 GJ. All rights reserved.
//

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

static const char KVOArrayKey;

@interface WTKVOItem: NSObject

@property (nonatomic, weak) id obj;

@property (nonatomic, strong) NSString *keyPath;


@end

@implementation WTKVOItem


@end

@implementation NSObject (KVO)

+ (void)load
{
    Method addObserver = class_getInstanceMethod(self, @selector(addObserver:forKeyPath:options:context:));
    Method wt_addObserver = class_getInstanceMethod(self, @selector(wt_addObserver:forKeyPath:options:context:));
    method_exchangeImplementations(addObserver, wt_addObserver);
    
    Method removeObserver = class_getInstanceMethod(self, @selector(removeObserver:forKeyPath:));
    Method wt_removeObserver = class_getInstanceMethod(self, @selector(wt_removeObserver:forKeyPath:));
    method_exchangeImplementations(removeObserver, wt_removeObserver);
}


- (void)wt_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    if (observer == nil || keyPath == nil || keyPath.length == 0) return;
    
    NSMutableDictionary *keyPathdict = objc_getAssociatedObject(observer, &KVOArrayKey);
    if (keyPathdict == nil)
    {
        keyPathdict = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(observer, &KVOArrayKey, keyPathdict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    WTKVOItem *item = [WTKVOItem new];
    item.keyPath = keyPath;
    item.obj = self;
    
    keyPathdict[keyPath] = item;

    [self wt_addObserver: observer forKeyPath: keyPath options: options context: context];
    
    
    [self replaceImpl: observer.class];
}

- (void)wt_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
    NSMutableDictionary *keyPaths = objc_getAssociatedObject(observer, &KVOArrayKey);
    if (keyPaths == nil) return;
    
    if ([keyPaths objectForKey: keyPath])
    {
        [self wt_removeObserver: observer forKeyPath: keyPath];
        
        [keyPaths removeObjectForKey: keyPath];
    }
}


- (void)wt_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context
{
    NSMutableDictionary *keyPaths = objc_getAssociatedObject(observer, &KVOArrayKey);
    if (keyPaths == nil) return;
    
    if ([keyPaths objectForKey: keyPath])
    {
        [self wt_removeObserver: observer forKeyPath: keyPath context: context];
        
        [keyPaths removeObjectForKey: keyPath];
    }
}

- (void)replaceImpl:(Class)cls
{
    Method dealloc = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
    
    __block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self){
        
        ((void(*)(id, SEL))objc_msgSend)(self, @selector(cleanupSEL));
        
        ((void(*)(id, SEL))deallocIMP)(self, NSSelectorFromString(@"dealloc"));
    
    }));
}

- (void)cleanupSEL
{
    NSMutableDictionary *keyPaths = objc_getAssociatedObject(self, &KVOArrayKey);
    if (keyPaths == nil) return;
    
    [keyPaths enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, WTKVOItem * _Nonnull obj, BOOL * _Nonnull stop) {
        
        [obj.obj removeObserver: self forKeyPath: obj.keyPath];
        
        
    }];
    
    objc_setAssociatedObject(self, &KVOArrayKey, NULL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


@end

上一篇下一篇

猜你喜欢

热点阅读