iOS開發(fā) - KVO監(jiān)聽, Block方式的實(shí)現(xiàn)

使用方法:

1,Block回調(diào)

    [self.data bindingWithKeyPath:@"number" WithBlock:^(id newValue, id oldValue) {
        self.label.text = [self.label.text setStr:newValue];
    }];

2,自動(dòng)改變對(duì)應(yīng)屬性

    [self.data bindingWithKeyPath:@"number" controlObjc:self.label objcKey:@"text" sync:NO]; 

一、介紹

本篇文章是介紹的是一種KVO是使用Block方式進(jìn)行回調(diào)的一種實(shí)現(xiàn)方式。
使用這種方式可以:更方便的使用KVO,可以利用到很多場(chǎng)景,進(jìn)行更簡(jiǎn)單響應(yīng)編程,可以進(jìn)行視圖和Model的一種綁定關(guān)系。

調(diào)用方式舉例:
    UILabel *label = [[UILabel alloc]init];
    [label bindingWithKeyPath:@"text" WithBlock:^(id newValue, id oldValue) {
        NSLog(@"%@",newValue);
    }];

演示:


二、核心原理

1,給NSOBject增加分類。
2,封裝observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context相關(guān)邏輯。
3,在內(nèi)部進(jìn)行實(shí)現(xiàn)KVO監(jiān)聽,利用屬性進(jìn)行存儲(chǔ)相應(yīng)的Key值、Block對(duì)象,通過對(duì)象的內(nèi)存地址和監(jiān)聽的key值,進(jìn)行拼接,生成唯一標(biāo)識(shí)符,通過標(biāo)識(shí)符,標(biāo)識(shí)對(duì)應(yīng)各自的Key、Block。
4,接收到監(jiān)聽后,通過自己的唯一標(biāo)識(shí)符,取出來自己的Block,進(jìn)行執(zhí)行后回調(diào)。

三、代碼實(shí)現(xiàn)

NSObject+DSSimpleKVO.h
#import <Foundation/Foundation.h>

typedef void (^ObserveBlock)(id newValue,id oldValue);

@interface NSObject (DSSimpleKVO)
/**
 模式 1 :綁定對(duì)象,監(jiān)聽屬性變化,進(jìn)行回調(diào)。
 
 @param key 需要監(jiān)聽的模型中的屬性名
 @param block 變化回調(diào)
 */
- (void)bindingWithKeyPath:(NSString *)key WithBlock:(ObserveBlock)block;
/**
 模式 2 :綁定對(duì)象,監(jiān)聽屬性變化,修改對(duì)應(yīng)屬性

 @param key 需要監(jiān)聽的模型中的屬性名
 @param controlObjc 需要修改的對(duì)象
 @param objcKey 需要修改的對(duì)象的屬性名
 @param sync 如果是字符串,是否進(jìn)行同步修改,或只修改數(shù)字。 YES 是完全同步修改, NO 是自動(dòng)替換數(shù)字
 */
- (void)bindingWithKeyPath:(NSString*)key controlObjc:(id)controlObjc objcKey:(NSString *)objcKey  sync:(BOOL)sync;

@end

@interface NSString (StringAdd)

- (NSString *)setStr:(NSString *)str;

@end
NSObject+DSSimpleKVO.m
#import <objc/runtime.h>
#import "NSObject+DSSimpleKVO.h"
#import "AppDelegate.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
#pragma clang diagnostic ignored "-Wenum-conversion"

#define DSFormat(format,objc) [NSString stringWithFormat:format,objc]

#define setAssignAssociated_MuDict if (!getAssociated()) {setValueAssociated(@{}.mutableCopy,Policy_Retain);}\
return getAssociated();

// 獲取關(guān)聯(lián)對(duì)象
#define getAssociated() objc_getAssociatedObject(self,_cmd)
// 設(shè)置assign類型的關(guān)聯(lián)
#define setAssignAssociated(value,value2,policy) objc_setAssociatedObject(self,@selector(value2),value,policy)
// 設(shè)置關(guān)聯(lián)的對(duì)象
#define setValueAssociated(objc,policy) objc_setAssociatedObject(self,_cmd,objc,((objc_AssociationPolicy)(policy)))
// 設(shè)置關(guān)聯(lián)
#define setAssociated(value,policy) objc_setAssociatedObject(self,@selector(value), value,((objc_AssociationPolicy)(policy)))

typedef NS_OPTIONS(NSUInteger, DSBindingType) {
    DSBindingKeyPath       = 0,
    DSBindingControl       = 1,
};

typedef OBJC_ENUM(uintptr_t, objc_Policy) {
    Policy_Assign = 0,              /**< Specifies a weak reference to the associated object. */
    Policy_Retain = 1,              /**< Specifies a strong reference to the associated object.*/
    Policy_Copy   = 3               /**< Specifies that the associated object is copied. */
};

@interface NSObject ()
@property (nonatomic, assign) BOOL                 sync;            // 是否同步進(jìn)行修改對(duì)應(yīng)對(duì)象屬性
@property (nonatomic, assign) DSBindingType        type;            // 當(dāng)前類型
@property (nonatomic, strong) NSString             *currentId;      // 唯一標(biāo)識(shí)符
@property (nonatomic, strong) NSMutableDictionary  *blockDict;      // 存儲(chǔ)Block字典

@property (nonatomic, strong) NSMutableDictionary  *controlDict;    // 控件對(duì)象字典(用于存儲(chǔ)--控件)
@property (nonatomic, strong) NSMutableDictionary  *propertyDict;   // 對(duì)象屬性字典(用于存儲(chǔ)--控件要修改的屬性名)
@property (nonatomic, strong) NSMutableDictionary  *identifierDict; // 唯一標(biāo)示字典(用于存儲(chǔ)對(duì)象相應(yīng)的唯一標(biāo)識(shí)符)

@end

@implementation NSObject (DSSimpleKVO)

/// 模式 1
- (void)bindingWithKeyPath:(NSString*)key WithBlock:(ObserveBlock)block
{
    NSAssert(block && key, @"參數(shù)不能為空");
    
    // 生成唯一標(biāo)識(shí)符,進(jìn)行賦值
    self.currentId = [self convertIdentifier:key];
    
    // 根據(jù)唯一標(biāo)識(shí)符,和Block,進(jìn)行對(duì)應(yīng)關(guān)系存儲(chǔ)
    [self.blockDict setValue:block forKey:self.currentId];
    
    [self setInitWithType:DSBindingKeyPath key:key];
}

/// 模式 2
- (void)bindingWithKeyPath:(NSString*)key controlObjc:(id)controlObjc objcKey:(NSString *)objcKey sync:(BOOL)sync
{
    NSAssert(controlObjc && objcKey, @"參數(shù)不能為空");
    
    self.currentId  = [self convertIdentifier:key];
    self.sync       = sync;
    
    [self.controlDict  setValue:controlObjc forKey:self.currentId];
    [self.propertyDict setValue:objcKey     forKey:self.currentId];

    [self setInitWithType:DSBindingControl key:key];
}

- (void)setInitWithType:(DSBindingType)type key:(NSString *)key{
    self.type = type;
    if (![self checkInput:key]) {
        return;
    }
    [self registPropertyObserver:key];
}

#pragma mark - 核心處理

/// 添加Observer監(jiān)聽
- (void)registPropertyObserver:(NSString*)key{

    // 判斷是否監(jiān)聽過
    if ([self.identifierDict valueForKey:self.currentId]) {
        return;
    }
    // 添加監(jiān)聽
    [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
              context:(__bridge void * _Nullable)(self.currentId)];
    [self.identifierDict setValue:self.currentId forKey:self.currentId];
}

#pragma mark - 事件處理
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    self.currentId = (__bridge NSString *)(context);
    [self processObserver:keyPath change:change];
}

- (void)processObserver:(NSString *)keyPath change:(NSDictionary *)change{
    
    id newValue = change[NSKeyValueChangeNewKey];
    id oldValue = change[NSKeyValueChangeOldKey];
    switch (self.type) {
        case DSBindingKeyPath:
        {
            ObserveBlock block = self.blockDict[self.currentId];
            block(newValue,oldValue);
        }
            break;
        case DSBindingControl:
        {
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象
            id objc     = self.controlDict[self.currentId];
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象對(duì)應(yīng)的屬性
            id property = self.propertyDict[self.currentId];
            // 通過kvc 給控件進(jìn)行賦值
            [objc setValue:[self checkString:newValue] forKey:property];
        }
            break;
        default:
            break;
    }
}

/// 自動(dòng)效驗(yàn)檢測(cè)中文,插入數(shù)據(jù)增加
- (NSString *)checkString:(NSString *)newValue{

    id objc       = self.controlDict[self.currentId];
    id property   = self.propertyDict[self.currentId];
    NSString *str = [objc valueForKey:property];
    
    // 進(jìn)行替換原字符的數(shù)字
    NSString *tempStr = [self repleaceStrWithOldStr:str newStr:newValue];
    
    return tempStr;
}

- (NSString *)repleaceStrWithOldStr:(NSString *)str newStr:(NSString *)newStr{

    NSString *oldReplaceStr = [self getNumberOfStrWithStr:str];
    // 進(jìn)行替換原字符的數(shù)字
    NSString *tempStr = [str stringByReplacingOccurrencesOfString:oldReplaceStr withString:newStr];
    
    if (![newStr isKindOfClass:[NSString class]]) {
        return newStr;
    }
    
    if (self.sync || ![self isPureNumandCharacters:newStr]) {
        return newStr;
    }
    
    return tempStr;
}

- (NSString *)getNumberOfStrWithStr:(NSString *)str{
    // 設(shè)置set過濾所有字符,只獲取數(shù)字
    NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    NSString *oldReplaceStr   = [str stringByTrimmingCharactersInSet:nonDigits];
    return oldReplaceStr;
}

- (BOOL)isPureNumandCharacters:(NSString *)string
{
    NSScanner* scan = [NSScanner scannerWithString:string];
    float val;
    int   val2;
    BOOL ffloat = [scan scanFloat:&val] && [scan isAtEnd];
    BOOL iint   = [scan scanInt:&val2]  && [scan isAtEnd];
    
    return ffloat || iint;
}

/// 刪除所有監(jiān)聽
- (void)removeAllObserver{
    // 如果沒有初始化好,那么就不往下執(zhí)行,防止引起崩潰。
    if (![UIApplication sharedApplication].delegate) {
        return;
    }
    if (!self.identifierDict.allKeys.count) {
        return;
    }
    NSArray *array = [self observers];
    for (NSString *key in array) {
        [self removeObserver:self forKeyPath:key];
    }
}

/// 獲取所有監(jiān)聽對(duì)象
- (NSArray *)observers
{
    NSArray *allKey = self.identifierDict.allKeys;
    NSMutableArray *tempArray = @[].mutableCopy;
    for (NSString *keys in allKey) {
       NSString *key = [keys componentsSeparatedByString:@"@@@"].lastObject;
       [tempArray addObject:key];
    }
    return tempArray;
}

/// 校驗(yàn)用戶輸入 和 防止多次添加
- (BOOL)checkInput:(id)key
{
    // 判斷是否存在該屬性
    @try {
        [self valueForKey:key];
    } @catch (NSException *exception) {
        NSLog(@"警告!對(duì)象沒有此屬性");
        return NO;
    }
    return YES;
}

/// -----  根據(jù)《對(duì)象內(nèi)存地址》和《key值》進(jìn)行生成唯一標(biāo)識(shí)符 -----
- (NSString *)convertIdentifier:(NSString *)key
{
    NSMutableString *objcAddress = DSFormat(@"%p@@@", self).mutableCopy;
    [objcAddress appendString:key];
    return objcAddress;
}

#pragma mark - Get 方法
- (BOOL)sync            {return [getAssociated() intValue];}
- (NSString *)currentId {return getAssociated();}
- (DSBindingType)type   {return (DSBindingType)[getAssociated() intValue];}

- (NSMutableDictionary *)blockDict{setAssignAssociated_MuDict}
- (NSMutableDictionary *)controlDict{setAssignAssociated_MuDict}
- (NSMutableDictionary *)propertyDict{setAssignAssociated_MuDict}
- (NSMutableDictionary *)identifierDict{setAssignAssociated_MuDict}

#pragma mark - Set 方法

- (void)setSync:(BOOL)sync{
    setAssignAssociated(@(sync), sync, Policy_Retain);
}
- (void)setType:(DSBindingType)type{
    setAssignAssociated(@(type), type, Policy_Retain);
}

- (void)setCurrentId:(NSString *)currentId{
    setAssociated(currentId, Policy_Retain);
}
- (void)setBlockDict:(NSMutableDictionary *)blockDict{
    setAssociated(blockDict, Policy_Retain);
}
- (void)setControlDict:(NSMutableDictionary *)controlDict
{
    setAssociated(controlDict, Policy_Retain);
}
- (void)setPropertyDict:(NSMutableDictionary *)propertyDict
{
    setAssociated(propertyDict, Policy_Retain);
}
- (void)setIdentifierDict:(NSMutableDictionary *)identifierDict
{
    setAssociated(identifierDict, Policy_Retain);
}
#pragma mark - 銷毀處理
- (void)dealloc{
    [self removeAllObserver];
}
@end

@implementation NSString (StringAdd)

- (NSString *)setStr:(NSString *)str{
    return [self repleaceStrWithOldStr:self newStr:str];
}
@end

三、代碼解析

1,使用Runtime 給分類動(dòng)態(tài)增加屬性,在這里為了更方便使用,就用宏定義封裝了一下runtime的關(guān)聯(lián),和宏定義自定義了一下,就不多說介紹了。

2,相關(guān)屬性

@property (nonatomic, assign) BOOL                 sync;            // 是否同步進(jìn)行修改對(duì)應(yīng)對(duì)象屬性
@property (nonatomic, assign) DSBindingType        type;            // 當(dāng)前類型
@property (nonatomic, strong) NSMutableDictionary  *blockDict;
@property (nonatomic, strong) NSString             *currentId;      // 唯一標(biāo)識(shí)符

@property (nonatomic, strong) NSMutableDictionary  *controlDict;    // 控件對(duì)象字典(用于存儲(chǔ)--控件
@property (nonatomic, strong) NSMutableDictionary  *propertyDict;   // 對(duì)象屬性字典(用于存儲(chǔ)--控件要修改的屬性名
@property (nonatomic, strong) NSMutableDictionary  *identifierDict; // 用于存儲(chǔ)對(duì)象相應(yīng)的唯一標(biāo)識(shí)符

(1)sync 和 type: 可以先忽略,后面會(huì)說到。
(2)blockDict 可變字典 : 用于存儲(chǔ)綁定keypath的時(shí)候傳遞進(jìn)來的KVO。
(3)currentId (唯一標(biāo)識(shí)符) :
思考一下,如果注冊(cè)多個(gè)KVO,那么Block回調(diào)的時(shí)候,到底是執(zhí)行哪個(gè)Block呢 ?
這個(gè)時(shí)候currentId的作用就出來了,它的作用是,當(dāng)使用者注冊(cè)多個(gè)kvo的時(shí)候,用于存儲(chǔ)block的時(shí)候,把currentid 當(dāng)做一個(gè)key值,做一個(gè)對(duì)應(yīng)關(guān)系,一個(gè)currentId 對(duì)應(yīng)一個(gè) block 。如:

[self.blockDict setValue:block forKey:self.currentId];

然后執(zhí)行在addObserver的時(shí)候進(jìn)行傳遞的一個(gè)參數(shù)context:(void *)context,把currentId傳遞進(jìn)去,當(dāng)屬性發(fā)生變化,observeValueForKeyPath 被調(diào)用的時(shí)候,根據(jù)傳遞進(jìn)來的context參數(shù),也就是currentID)當(dāng)做key值,取出對(duì)應(yīng)Block,然后執(zhí)行。如:

ObserveBlock block = self.blockDict[self.currentId];
block(newValue,oldValue);

(4)identifierDict :作用是防止多次重復(fù)執(zhí)行addObserver 進(jìn)行監(jiān)聽,最明顯的是當(dāng)使用UITableViewCell的時(shí)候,Cell會(huì)進(jìn)行復(fù)用,這樣如果不做處理,默認(rèn)會(huì)走很多次set方法,如果在里面添加了KVO,不做處理的話,那么就會(huì)導(dǎo)致添加了很多監(jiān)聽,就會(huì)出問題。identifierDict的作用就是為了解決這種情況,只要添加到一次監(jiān)聽,那么identifierDict 就會(huì)把 currentId 當(dāng)做唯一標(biāo)識(shí)符進(jìn)行存儲(chǔ),如果下次,添加KVO之前,會(huì)從identifierDict 里判斷是否已經(jīng)存在過當(dāng)前currentId,如果存在那么久不做任何操作。

 // 判斷是否監(jiān)聽過
    if ([self.identifierDict valueForKey:self.currentId]) {
        NSLog(@"已經(jīng)監(jiān)聽過了,防止重復(fù)監(jiān)聽");
        return;
    }
    
    // 添加監(jiān)聽
    [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
              context:(__bridge void * _Nullable)(self.currentId)];
    
    [self.identifierDict setValue:self.currentId forKey:self.currentId];

(5)controlDict 、 propertyDict 和 blockDict 的作用差不多,只不過是存儲(chǔ)對(duì)象 和 屬性名的,后面會(huì)說到。

3,調(diào)用方法

UILabel *label = [[UILabel alloc]init];
[label bindingWithKeyPath:@"text" WithBlock:^(id newValue, id oldValue) {
     NSLog(@"%@",newValue);
}];

只需要傳遞進(jìn)來一個(gè)屬性,和block即可。

4,調(diào)用內(nèi)部實(shí)現(xiàn)
bindingWithKeyPath 這個(gè)主要是存儲(chǔ)唯一標(biāo)識(shí)符,然后存儲(chǔ)Block后,繼續(xù)下面的邏輯

- (void)bindingWithKeyPath:(NSString*)key WithBlock:(ObserveBlock)block
{
    NSAssert(block && key, @"參數(shù)不能為空");

    // 生成唯一標(biāo)識(shí)符,進(jìn)行賦值
    self.currentId = [self convertIdentifier:key];
    
   // 根據(jù)唯一標(biāo)識(shí)符,和Block,進(jìn)行對(duì)應(yīng)關(guān)系存儲(chǔ)
    [self.blockDict setValue:block forKey:self.currentId];
    
    [self setInitWithType:DSBindingKeyPath key:key];
}

唯一標(biāo)識(shí)符,本來是需要用戶手動(dòng)傳入的,為了做到不讓用戶手動(dòng)傳入唯一標(biāo)識(shí)符,在這里做了自動(dòng)生成唯一標(biāo)識(shí)符:

/// -----  根據(jù)《對(duì)象內(nèi)存地址》和《key值》進(jìn)行生成唯一標(biāo)識(shí)符 -----
- (NSString *)convertIdentifier:(NSString *)key
{
    NSMutableString *objcAddress = DSFormat(@"%p@@@", self).mutableCopy;
    [objcAddress appendString:key];
    return objcAddress;
}

setInitWithType 主要是校驗(yàn) 對(duì)象有沒有此屬性,里面使用try catch 方法。

- (void)setInitWithType:(DSBindingType)type key:(NSString *)key{
    self.type = type;
    if (![self checkInput:key]) {
        return;
    }
    [self registPropertyObserver:key];
}

registPropertyObserver 主要就是核心添加kvo的方法,和添加后進(jìn)行存儲(chǔ)currentId唯一標(biāo)識(shí)符。

/// 添加Observer監(jiān)聽
- (void)registPropertyObserver:(NSString*)key{

    // 判斷是否監(jiān)聽過
    if ([self.identifierDict valueForKey:self.currentId]) {
        return;
    }
    // 添加監(jiān)聽
    [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
              context:(__bridge void * _Nullable)(self.currentId)];
    [self.identifierDict setValue:self.currentId forKey:self.currentId];
}

observeValueForKeyPath 和 processObserver 就是回調(diào)之后的操作

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    self.currentId = (__bridge NSString *)(context);
    [self processObserver:keyPath change:change];
}

- (void)processObserver:(NSString *)keyPath change:(NSDictionary *)change{
    id newValue = change[NSKeyValueChangeNewKey];
    id oldValue = change[NSKeyValueChangeOldKey];
    switch (self.type) {
        case DSBindingKeyPath:
        {
            ObserveBlock block = self.blockDict[self.currentId];
            block(newValue,oldValue);
        }
            break;
        case DSBindingControl:
        {
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象
            id objc     = self.controlDict[self.currentId];
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象對(duì)應(yīng)的屬性
            id property = self.propertyDict[self.currentId];
            // 通過kvc 給控件進(jìn)行賦值
            [objc setValue:[self checkString:newValue] forKey:property];
        }
            break;
        default:
            break;
    }
}

自動(dòng)刪除監(jiān)聽的邏輯
在dealloc中,進(jìn)行遍歷identifierDict,取出當(dāng)前監(jiān)聽的key,進(jìn)行remove。

/// 刪除所有監(jiān)聽
- (void)removeAllObserver{
    // 如果沒有初始化好,那么就不往下執(zhí)行,防止引起崩潰。
    if (![UIApplication sharedApplication].delegate) {
        return;
    }
    if (!self.identifierDict.allKeys.count) {
        return;
    }
    NSArray *array = [self observers];
    for (NSString *key in array) {
        [self removeObserver:self forKeyPath:key];
    }
}

到這里基本的核心功能就說完了,大概就這些。
三、 模式2 功能
從最初就提到了 模式2 和 controlDict 、 propertyDict 這些東西,現(xiàn)在,在這里說明一下模式2 。

1,模式2 的作用
模式2 的作用主要是相對(duì)于某些場(chǎng)景,更方便的一種解決方法,使用模式2,可以做到,監(jiān)聽某對(duì)象屬性后,變化后,可指定對(duì)應(yīng)對(duì)象的屬性,也進(jìn)行同步進(jìn)行變化,也可以是如果是需要這種情況的下,就相對(duì)于第一種模式,不用寫block,而是直接自動(dòng)修改。比如下圖例子:


sync 屬性的作用是,如果設(shè)置為YES,那么在監(jiān)聽發(fā)生變化的時(shí)候,會(huì)把newValue 進(jìn)行完全賦值給另一個(gè)對(duì)象的屬性,如果設(shè)置為NO,那么就會(huì)只替換數(shù)字的那一部分。 方便于某些場(chǎng)景的使用。

2,調(diào)用例子


// 監(jiān)聽 《self.data》對(duì)象 的 《number》屬性, number 屬性變化后,自動(dòng) 修改 《self.label》對(duì)象的《text》屬性
[self.data bindingWithKeyPath:@"number" controlObjc:self.label objcKey:@"text" sync:NO];

3,實(shí)現(xiàn)
在分類中增加了另一個(gè)方法bindingWithKeyPath:(NSString*)key controlObjc:(id)controlObjc objcKey:(NSString *)objcKey sync:(BOOL)sync;
key :是需要監(jiān)聽的key
controlObjc:是監(jiān)聽后變化通知的對(duì)象
objcKey :變化通知對(duì)象需要修改的屬性
sync: 就是如上面所說的,是否進(jìn)行完全復(fù)制。

和模式1現(xiàn)在區(qū)別主要是多了存儲(chǔ)通知修改的對(duì)象,和對(duì)象的屬性,和sync屬性,在調(diào)用方法后,分別使用controlDict 存儲(chǔ)對(duì)象,和propertyDict 存儲(chǔ)對(duì)象的屬性。type 用于區(qū)分,是模式一還是模式二。

    [self.controlDict  setValue:controlObjc forKey:self.currentId];
    [self.propertyDict setValue:objcKey     forKey:self.currentId];

在接受到變化的時(shí)候,進(jìn)行分別取出對(duì)應(yīng)的對(duì)象和屬性,進(jìn)行賦值:

  case DSBindingControl:
        {
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象
            id objc     = self.controlDict[self.currentId];
            // 根據(jù)唯一標(biāo)識(shí)符取出來對(duì)象對(duì)應(yīng)的屬性
            id property = self.propertyDict[self.currentId];
            // 通過kvc 給控件進(jìn)行賦值
            [objc setValue:[self checkString:newValue] forKey:property];
        }

關(guān)于sync屬性,設(shè)置自動(dòng)替換數(shù)字的原理,主要是檢測(cè)字符串,獲取字符串中的數(shù)字,然后通過stringByReplacingOccurrencesOfString 進(jìn)行只把新值和數(shù)字進(jìn)行替換。如果新值不是數(shù)字,那么也進(jìn)行完全替換。

/// 自動(dòng)效驗(yàn)檢測(cè)中文,插入數(shù)據(jù)增加
- (NSString *)checkString:(NSString *)newValue{

    id objc       = self.controlDict[self.currentId];
    id property   = self.propertyDict[self.currentId];
    NSString *str = [objc valueForKey:property];
    
    // 進(jìn)行替換原字符的數(shù)字
    NSString *tempStr = [self repleaceStrWithOldStr:str newStr:newValue];
    
    return tempStr;
}

- (NSString *)repleaceStrWithOldStr:(NSString *)str newStr:(NSString *)newStr{

    NSString *oldReplaceStr = [self getNumberOfStrWithStr:str];
    // 進(jìn)行替換原字符的數(shù)字
    NSString *tempStr = [str stringByReplacingOccurrencesOfString:oldReplaceStr withString:newStr];
    
    if (![newStr isKindOfClass:[NSString class]]) {
        return newStr;
    }
    
    if (self.sync || ![self isPureNumandCharacters:newStr]) {
        return newStr;
    }
    
    return tempStr;
}

- (NSString *)getNumberOfStrWithStr:(NSString *)str{
    // 設(shè)置set過濾所有字符,只獲取數(shù)字
    NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    NSString *oldReplaceStr   = [str stringByTrimmingCharactersInSet:nonDigits];
    return oldReplaceStr;
}

isPureNumandCharacters 是判斷是否是數(shù)字的函數(shù)

- (BOOL)isPureNumandCharacters:(NSString *)string
{
    NSScanner* scan = [NSScanner scannerWithString:string];
    float val;
    int   val2;
    BOOL ffloat = [scan scanFloat:&val] && [scan isAtEnd];
    BOOL iint   = [scan scanInt:&val2]  && [scan isAtEnd];
    
    return ffloat || iint;
}

四、最后

GitHub鏈接地址: https://github.com/bigsen/DSKVO
思路和實(shí)現(xiàn)就是這樣,主要核心思想很簡(jiǎn)單,目前沒有大量測(cè)試,不知道有沒有BUG。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容