成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

KVC原理與數(shù)據(jù)篩選

開(kāi)發(fā) 前端
基本的訪問(wèn)器方法、變量的查找和異常處理已經(jīng)清楚的知道了。那么上面的例子是如何出現(xiàn)的呢?明明傳入的是字符串,最后賦值的時(shí)候轉(zhuǎn)變?yōu)樵L問(wèn)器方法所對(duì)應(yīng)的類型?繼續(xù)刨根問(wèn)底!

1、前言

在技術(shù)論壇中看到一則很有意思的KVC案例:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
Person *person = [Person new];
person.name = @"Tom";
person.age = 10;
[person setValue:@"100" forKey:@"age"];//此處賦值為字符串,類中屬性為Integer

第一反應(yīng)是崩潰,因?yàn)镺C是類型敏感的??墒亲约簩?shí)現(xiàn)并打印后的結(jié)果出于意料,沒(méi)有崩潰且賦值成功。所以有了深入了解KVC的內(nèi)部實(shí)現(xiàn)的想法!

2、什么是KVC

key-value-coding:鍵值編碼,一種可以通過(guò)鍵名間接訪問(wèn)和賦值對(duì)象屬性的機(jī)制
KVC是通過(guò)NSObject、NSArray、NSDictionary等的類別來(lái)實(shí)現(xiàn)的
主要方法包括一下幾個(gè):

- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (void)setNilValueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
- (nullable id)valueForUndefinedKey:(NSString *)key;

3、KVC執(zhí)行分析

那么上面的案例中的- (void)setValue:(nullable id)value forKey:(NSString *)key;是怎樣的執(zhí)行過(guò)程呢?借助反匯編工具獲得Foundation.framework部分源碼(為了解決和系統(tǒng)API沖突問(wèn)題增加前綴_d,NS替換為DS),以此分析KVC執(zhí)行過(guò)程。(流程中的邊界判斷等已經(jīng)忽略,如想了解可以參考源碼,本文只探究主流程。)

3.1 設(shè)置屬性

3.1.1 查找訪問(wèn)器方法或成員變量

+ (DSKeyValueSetter *)_d_createValueSetterWithContainerClassID:(id)containerClassID key:(NSString *)key {
DSKeyValueSetter *setter = nil;
char key_cstr_upfirst[key_cstr_len + 1];
key_cstr[key_cstr_len + 1];
...
Method method = NULL;
//按順序?qū)ふ襰et<Key>,_set<Key>,setIs<Key>。找到后則生成對(duì)應(yīng)的seter
if ((method = DSKeyValueMethodForPattern(self, "set%s:", key_cstr_upfirst)) ||
(method = DSKeyValueMethodForPattern(self, "_set%s:", key_cstr_upfirst)) ||
(method = DSKeyValueMethodForPattern(self, "setIs%s:", key_cstr_upfirst))
) { //生成Method:包含selector,IMP。返回和參數(shù)類型字符串
setter = [[DSKeyValueMethodSetter alloc] initWithContainerClassID:containerClassID key:key method:method];
} else if ([self accessInstanceVariablesDirectly]) {//如果沒(méi)有找到對(duì)應(yīng)的訪問(wèn)器方且工廠方法accessInstanceVariablesDirectly == ture ,則按照順序查找查找成員變量_<key>,_is<Key>,<key>,is<Key>(注意key的首字母大小寫,查找到則生成對(duì)應(yīng)的setter)
Ivar ivar = NULL;
if ((ivar = DSKeyValueIvarForPattern(self, "_%s", key_cstr)) ||
(ivar = DSKeyValueIvarForPattern(self, "_is%s", key_cstr_upfirst)) ||
(ivar = DSKeyValueIvarForPattern(self, "%s", key_cstr)) ||
(ivar = DSKeyValueIvarForPattern(self, "is%s", key_cstr_upfirst))
) {
setter = [[DSKeyValueIvarSetter alloc] initWithContainerClassID:containerClassID key:key containerIsa:self ivar:ivar];
}
}
...
return setter;
}

查找順序如下:

  1. 查找訪問(wèn)器方法:set,_set,setIs
  2. 如果步驟1中沒(méi)找到對(duì)應(yīng)的方法且 accessInstanceVariablesDirectly == YES

則查找順序如下:_,_is,,is
查找不到則調(diào)用valueForUndefinedKey并拋出異常

3.1.2 生成setter

+ (DSKeyValueSetter *)_d_createOtherValueSetterWithContainerClassID:(id)containerClassID key:(NSString *)key {
return [[DSKeyValueUndefinedSetter alloc] initWithContainerClassID:containerClassID key:key containerIsa:self];
}
//構(gòu)造方法確定方法編號(hào) d_setValue:forUndefinedKey: 和方法指針I(yè)MP _DSSetValueAndNotifyForUndefinedKey
- (id)initWithContainerClassID:(id)containerClassID key:(NSString *)key containerIsa:(Class)containerIsa {
...
return [super initWithContainerClassID:containerClassID key:key implementation:method_getImplementation(class_getInstanceMethod(containerIsa, @selector(d_setValue:forUndefinedKey:))) selector:@selector(d_setValue:forUndefinedKey:) extraArguments:arguments count:1];
}

3.1.3 賦值

基本的訪問(wèn)器方法、變量的查找和異常處理已經(jīng)清楚的知道了。那么上面的例子是如何出現(xiàn)的呢?明明傳入的是字符串,最后賦值的時(shí)候轉(zhuǎn)變?yōu)樵L問(wèn)器方法所對(duì)應(yīng)的類型?繼續(xù)刨根問(wèn)底!

DSKeyValueSetter對(duì)象已經(jīng)生成,即確定了發(fā)送消息的對(duì)象object、訪問(wèn)器方法名SEL、訪問(wèn)器函數(shù)指針I(yè)MP、以及使用KVC時(shí)傳入的Key和Value。下面進(jìn)入方法調(diào)用階段:_DSSetUsingKeyValueSetter(self,setter, value);

IMP指針為_(kāi)DSSetIntValueForKeyWithMethod其定義如下:之所以有文章開(kāi)頭提到的效果就是這里起了作用,在IMP調(diào)用的時(shí)候做了[value valueGetSelectorName],將對(duì)應(yīng)的NSNumber轉(zhuǎn)換為簡(jiǎn)單數(shù)據(jù)類型。這里是intValue。

void _DSSetIntValueForKeyWithMethod(id object, SEL selector,id value, NSString *key, Method method) {// object:person selector:setAge:  value:@(100)  key:age  method:selector + IMP + 返回類型和參數(shù)類型 即_extraArgument2,其在第一步查找到訪問(wèn)器方法后生成
__DSSetPrimitiveValueForKeyWithMethod(object, selector, value, key, method, int, intValue);
}
#define
if (value) {\
void (*imp)(id,SEL,valueType) = (void (*)(id,SEL,valueType))method_getImplementation(method);\
imp(object, method_getName(method), [value valueGetSelectorName]);\調(diào)用person的setAge:方法。參數(shù)為100
}\
else {\
[object setNilValueForKey:key];\
}\
}while(0)
//如果第一步中沒(méi)有找到訪問(wèn)器方法只找到了成員變量則直接執(zhí)行賦值操作
void _DSSetIntValueForKeyInIvar(id object, SEL selector, id value, NSString *key, Ivar ivar) {
if (value) {
*(int *)object_getIvarAddress(object, ivar) = [value intValue];
}
else {
[object setNilValueForKey:key];
}
}

起始問(wèn)題完美解決!執(zhí)行流程如下:

3.2 取值

3.2.1 查找訪問(wèn)器方法或成員變量

+ (DSKeyValueGetter *)_d_createValueGetterWithContainerClassID:(id)containerClassID key:(NSString *)key {
DSKeyValueGetter * getter = nil;
...
Method getMethod = NULL;
if((getMethod = DSKeyValueMethodForPattern(self,"get%s",keyCStrUpFirst)) ||
(getMethod = DSKeyValueMethodForPattern(self,"%s",keyCStr)) ||
(getMethod = DSKeyValueMethodForPattern(self,"is%s",keyCStrUpFirst)) ||
(getMethod = DSKeyValueMethodForPattern(self,"_get%s",keyCStrUpFirst)) ||
(getMethod = DSKeyValueMethodForPattern(self,"_%s",keyCStr))) {
getter = [[DSKeyValueMethodGetter alloc] initWithContainerClassID:containerClassID key:key method:getMethod];
}// 查找對(duì)應(yīng)的訪問(wèn)器方法
...
else if([self accessInstanceVariablesDirectly]) {//查找屬性
Ivar ivar = NULL;
if((ivar = DSKeyValueIvarForPattern(self, "_%s", keyCStr)) ||
(ivar = DSKeyValueIvarForPattern(self, "_is%s", keyCStrUpFirst)) ||
(ivar = DSKeyValueIvarForPattern(self, "%s", keyCStr)) ||
(ivar = DSKeyValueIvarForPattern(self, "is%s", keyCStrUpFirst))
) {
getter = [[DSKeyValueIvarGetter alloc] initWithContainerClassID:containerClassID key:key containerIsa:self ivar:ivar];
}
}
}
if(!getter) {
getter = [self _d_createValuePrimitiveGetterWithContainerClassID:containerClassID key:key];
}
return getter;
}

  1. 按照get,,is,_的順序查找成員方法
  2. 如果1.沒(méi)有找到對(duì)應(yīng)的方法且accessInstanceVariablesDirectly==YES,則繼續(xù)查找成員變量,查找順序?yàn)開(kāi),_is,,is
  3. 如果1,2沒(méi)有找到對(duì)應(yīng)的方法和屬性則調(diào)用 valueForUndefinedKey:并拋出異常

3.2.2 如上步驟沒(méi)定位到訪問(wèn)器方法或成員變量則走下面的流程生成對(duì)應(yīng)的getter

訪問(wèn)器方法生成IMP
- (id)initWithContainerClassID:(id)containerClassID key:(NSString *)key method:(Method)method {
NSUInteger methodArgumentsCount = method_getNumberOfArguments(method);
NSUInteger extraAtgumentCount = 1;
if(methodArgumentsCount == 2) {
char *returnType = method_copyReturnType(method);
IMP imp = NULL;
switch (returnType[0]) {
...
case 'i': {
imp = (IMP)_DSGetIntValueWithMethod;
} break;
...
free(returnType);
if(imp) {
void *arguments[3] = {0};
if(extraAtgumentCount > 0) {
arguments[0] = method;
}
return [super initWithContainerClassID:containerClassID key:key implementation:imp selector:method_getName(method) extraArguments:arguments count:extraAtgumentCount];
}
}

單步調(diào)試后可以看到具體的IMP類型

定義如下:

NSNumber * _DSGetIntValueWithMethod(id object, SEL selctor, Method method) {// 
return [[[NSNumber alloc] initWithInt: ((int (*)(id,SEL))method_getImplementation(method))(object, method_getName(method))] autorelease];
}

3.2.3 取值

取值調(diào)用如下:

4、簡(jiǎn)單數(shù)據(jù)類型KVC包裝和拆裝關(guān)系

NSNunber:

NSValue

5、KVC高級(jí)

修改數(shù)組中對(duì)象的屬性[array valueForKeyPath:@”uppercaseString”]利用KVC可以批量修改屬性的成員變量值

求和,平均數(shù),最大值,最小值NSNumbersum= [array valueForKeyPath:@”@sum.self”];NSNumberavg= [array valueForKeyPath:@”@avg.self”];NSNumbermax= [array valueForKeyPath:@”@max.self”];NSNumbermin= [array valueForKeyPath:@”@min.self”];

6、數(shù)據(jù)篩選

經(jīng)過(guò)上面的分析可以明白KVC的真正執(zhí)行流程。下面結(jié)合日常工程中的實(shí)際應(yīng)用來(lái)優(yōu)雅的處理數(shù)據(jù)篩選問(wèn)題。使用KVC處理可以減少大量for的使用并增加代碼可讀性和健壯性。
如圖所示:

項(xiàng)目中的細(xì)節(jié)如下:修改拒收數(shù)量時(shí)更新總妥投數(shù)和總拒收數(shù)、勾選明細(xì)更新總妥投數(shù)和總拒收數(shù)、全選、清空、反選。如果用通常的做法是每次操作都要循環(huán)去計(jì)算總數(shù)和記錄選擇狀態(tài)。下面是采用KVC的實(shí)現(xiàn)過(guò)程。
模型涉及:

@property (nonatomic,copy)NSString* skuCode;
@property (nonatomic,copy)NSString* goodsName;
@property (nonatomic,assign)NSInteger totalAmount;
@property (nonatomic,assign)NSInteger rejectAmount;
@property (nonatomic,assign)NSInteger deliveryAmount;
///單選用
@property (nonatomic, assign) BOOL selected;

1)更新總數(shù)

- (void)updateDeliveryInfo {
//總數(shù)
NSNumber *allDeliveryAmount = [self.orderDetailModel.deliveryGoodsDetailList valueForKeyPath:@"@sum.totalAmount"];
//妥投數(shù)
NSNumber *allRealDeliveryAmount = [self.orderDetailModel.deliveryGoodsDetailList valueForKeyPath:@"@sum.deliveryAmount"];
//拒收數(shù)
NSNumber *allRejectAmount = [self.orderDetailModel.deliveryGoodsDetailList valueForKeyPath:@"@sum.rejectAmount"];
}

2)全選
[self.orderDetailModel.deliveryGoodsDetailList setValue:@(YES) forKeyPath:@”selected”];

3)清空
[self.orderDetailModel.deliveryGoodsDetailList setValue:@(NO) forKeyPath:@”selected”];

4)反選

NSPredicate *selectedPredicate = [NSPredicate predicateWithFormat:@"selected == %@",@(YES)];
NSArray *selectedArray = [self.orderDetailModel.deliveryGoodsDetailList filteredArrayUsingPredicate:selectedPredicate];
NSPredicate *unSelectedPredicate = [NSPredicate predicateWithFormat:@"selected == %@",@(NO)];
NSArray *unSelectedArray = [self.orderDetailModel.deliveryGoodsDetailList filteredArrayUsingPredicate:unSelectedPredicate];
[selectedArray setValue:@(NO) forKeyPath:@"selected"];
[unSelectedArray setValue:@(YES) forKeyPath:@"selected"];

7、總結(jié)

KVC在處理簡(jiǎn)單數(shù)據(jù)類型時(shí)會(huì)經(jīng)過(guò)數(shù)據(jù)封裝和拆裝并轉(zhuǎn)換為對(duì)應(yīng)的數(shù)據(jù)類型。通過(guò)KVC的特性我們可以在日常使用中更加優(yōu)雅的對(duì)數(shù)據(jù)進(jìn)行篩選和處理。優(yōu)點(diǎn)如下:可閱讀性更高,健壯性更好。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2025-03-03 10:28:38

2020-06-17 12:36:45

查詢數(shù)據(jù)Python

2011-07-25 17:07:16

iPhone KVO KVC

2023-05-08 00:08:51

Hive機(jī)制場(chǎng)景

2023-07-31 08:39:19

MongoDB查詢語(yǔ)法

2013-10-24 09:40:31

CommVault數(shù)據(jù)保護(hù)歸檔

2017-02-09 17:00:00

iOSSwiftKVC

2023-11-13 23:06:52

Android序列化

2022-06-30 10:00:28

數(shù)據(jù)系統(tǒng)

2023-06-08 07:25:56

數(shù)據(jù)庫(kù)索引數(shù)據(jù)結(jié)構(gòu)

2021-05-06 08:55:24

ThreadLocal多線程多線程并發(fā)安全

2011-04-21 10:06:40

SQL篩選

2020-03-05 09:33:15

數(shù)據(jù)庫(kù)事務(wù)隔離事務(wù)

2022-06-13 16:09:17

PandasPython

2013-09-22 14:02:09

內(nèi)存數(shù)據(jù)庫(kù)

2013-05-13 17:33:10

2020-05-22 09:12:46

HTTP3網(wǎng)絡(luò)協(xié)議

2014-12-18 13:20:09

Docker容器鏡像數(shù)據(jù)卷

2025-02-26 07:59:47

2023-02-07 09:17:19

Java注解原理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品99久久久久久久vr | 亚洲精品免费视频 | 中文字幕的av | 91免费小视频 | 亚洲精品第一国产综合野 | 欧美一区成人 | 美美女高清毛片视频免费观看 | 亚洲国内精品 | 久久专区| 二区av | 成人免费网站在线 | www.888www看片 | 国产视频一区在线 | 国产亚洲人成a在线v网站 | 精品网| 成人毛片网 | 精品在线观看一区 | 午夜伦理影院 | 日日操日日干 | 午夜无码国产理论在线 | 午夜久久久 | 波多野结衣中文视频 | 日韩在线国产 | 亚洲色在线视频 | 视频一区二区中文字幕 | 亚洲精品电影 | 91婷婷韩国欧美一区二区 | 人人鲁人人莫人人爱精品 | 欧美一区二区三区日韩 | 狠狠操婷婷 | 国产超碰人人爽人人做人人爱 | 色综合欧美 | 久久国产精品99久久久久久丝袜 | 国产激情免费视频 | 99国产精品久久久久老师 | 欧美日韩三区 | 日韩午夜一区二区三区 | 一区二区三区在线 | 婷婷不卡 | 亚洲欧洲一区二区 | 密色视频 |