
使用方法:
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。