版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.09.09 |
前言
KVC
相信大家再熟悉不過了稀轨,鍵值編碼扼脐,可以解決很多問題,包括視圖上的給UITextField
占位文字顏色大小進(jìn)行設(shè)置等等,還有很多地方可以用KVC瓦侮,接下來幾篇我們就深度解析一下KVC艰赞。總結(jié)一下方妖,就是指iOS的開發(fā)中罚攀,可以允許開發(fā)者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值斋泄。而不需要調(diào)用明確的存取方法杯瞻。這樣就可以在運(yùn)行時(shí)動(dòng)態(tài)地訪問和修改對象的屬性。而不是在編譯時(shí)確定炫掐,這也是iOS開發(fā)中的黑魔法之一。還是老規(guī)矩,由面到點(diǎn)宙地,由淺到深摔认,希望對大家有所幫助。感興趣的可以看我寫的另外幾篇文章宅粥。
1. KVC解析(一) —— 基本了解
2. KVC解析(二) —— 不可不知的賦值深層次原理
3. KVC解析(三) —— 不可不知的取值深層次原理
4. KVC解析(四) —— keyPath的深度解析
為什么要做異常處理?
在使用KVC的時(shí)候抹蚀,由于人為操作的錯(cuò)誤企垦,很多時(shí)候會(huì)碰到異常,這就需要我們?nèi)斯みM(jìn)行干預(yù)钞诡,否則就會(huì)崩潰,常見的比如說找key的時(shí)候出現(xiàn)錯(cuò)誤等接箫,這些需要我們進(jìn)行處理朵诫。
幾種典型的異常處理
1. 找不到key
這種情況就會(huì)直接奔潰,拋出異常废累,比如說:
#import "JJKVCExceptionVC.h"
@interface JJKVCExceptionVC ()
@property (nonatomic, copy) NSString *name;
@end
@implementation JJKVCExceptionVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self demoExceptionOne];
}
#pragma mark - Object Private Function
//異常場景1:找不到對應(yīng)的key
- (void)demoExceptionOne
{
[self setValue:nil forKey:@"person"];
}
@end
下面看輸出結(jié)果
2017-09-09 11:40:08.107 JJOC[3372:86627] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<JJKVCExceptionVC 0x7fd09cf04bc0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key person.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000105ba9b0b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000105230141 objc_exception_throw + 48
2 CoreFoundation 0x0000000105ba9a59 -[NSException raise] + 9
3 Foundation 0x0000000104d4600b -[NSObject(NSKeyValueCoding) setValue:forKey:] + 292
4 UIKit 0x00000001075fc994 -[UIViewController setValue:forKey:] + 87
5 JJOC 0x00000001048e1206 -[JJKVCExceptionVC demoExceptionOne] + 54
6 JJOC 0x00000001048e11bd -[JJKVCExceptionVC viewDidLoad] + 189
7 UIKit 0x000000010760401a -[UIViewController loadViewIfRequired] + 1235
8 UIKit 0x0000000107642e6c -[UINavigationController _layoutViewController:] + 56
9 UIKit 0x000000010764374a -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 466
10 UIKit 0x00000001076438bb -[UINavigationController _startTransition:fromViewController:toViewController:] + 127
11 UIKit 0x0000000107644a03 -[UINavigationController _startDeferredTransitionIfNeeded:] + 843
12 UIKit 0x0000000107645b41 -[UINavigationController __viewWillLayoutSubviews] + 58
13 UIKit 0x000000010783760c -[UILayoutContainerView layoutSubviews] + 231
14 UIKit 0x000000010752455b -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1268
15 QuartzCore 0x00000001070a3904 -[CALayer layoutSublayers] + 146
16 QuartzCore 0x0000000107097526 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 370
17 QuartzCore 0x00000001070973a0 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
18 QuartzCore 0x0000000107026e92 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
19 QuartzCore 0x0000000107053130 _ZN2CA11Transaction6commitEv + 468
20 QuartzCore 0x0000000107053b37 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 115
21 CoreFoundation 0x0000000105b4f717 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
22 CoreFoundation 0x0000000105b4f687 __CFRunLoopDoObservers + 391
23 CoreFoundation 0x0000000105b34038 CFRunLoopRunSpecific + 440
24 UIKit 0x000000010745b08f -[UIApplication _run] + 468
25 UIKit 0x0000000107461134 UIApplicationMain + 159
26 JJOC 0x00000001048edcaf main + 111
27 libdyld.dylib 0x0000000109d9f65d start + 1
)
這個(gè)就是找不到key的崩潰震放,怎么處理這個(gè)異常呢驼修?其實(shí)可以重寫下面方法乙各。
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"找不到key的異常可以在這里處理");
}
在重寫的方法里面可以處理找不到key的情況耳峦,但是實(shí)際上不這么做,應(yīng)該盡量避免這種錯(cuò)誤驶乾,直接復(fù)制粘貼用到的屬性循签,不要手動(dòng)敲,容易出錯(cuò)风科。
2. 傳值為nil
這總錯(cuò)誤不會(huì)崩潰乞旦,但是很隱蔽,不容易發(fā)現(xiàn)故痊。
下面還是看代碼
#import "JJKVCExceptionVC.h"
@interface JJKVCExceptionVC ()
@property (nonatomic, copy) NSString *name;
@end
@implementation JJKVCExceptionVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self demoExceptionTwo];
}
#pragma mark - Object Private Function
//異常場景2:傳值傳nil
- (void)demoExceptionTwo
{
[self setValue:nil forKey:@"name"];
NSLog(@"name = %@", self.name);
}
@end
下面看輸出結(jié)果
2017-09-09 11:53:36.333 JJOC[4101:99896] name = (null)
可見玖姑,你value即使傳入的是nil,但是程序也不會(huì)崩潰豫领,他會(huì)自動(dòng)的給這個(gè)屬性賦值為null舔琅。這里之所以不崩潰是因?yàn)槲叶x的屬性name屬于對象,對象可以有nil類型课蔬,但是如果我定義的屬性是非對象的呢,下面看一下驗(yàn)證代碼战惊。
#import "JJKVCExceptionVC.h"
@interface JJKVCExceptionVC ()
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation JJKVCExceptionVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self demoExceptionTwo];
}
#pragma mark - Object Private Function
//異常場景2:傳值傳nil
- (void)demoExceptionTwo
{
[self setValue:nil forKey:@"age"];
NSLog(@"name = %ld", self.age);
}
@end
這里age我定義的屬性不是對象扎即,而是一個(gè)整型,下面我們運(yùn)行你就會(huì)發(fā)現(xiàn)這次不能運(yùn)行了各拷,崩潰并拋出異常了闷营,如下所示:
2017-09-09 12:00:01.072 JJOC[4673:106219] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<JJKVCExceptionVC 0x7f92d6506440> setNilValueForKey]: could not set nil as the value for the key age.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010ced4b0b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010c55b141 objc_exception_throw + 48
2 CoreFoundation 0x000000010cf3d625 +[NSException raise:format:] + 197
3 Foundation 0x000000010c139884 -[NSObject(NSKeyValueCoding) setNilValueForKey:] + 81
4 Foundation 0x000000010c07100b -[NSObject(NSKeyValueCoding) setValue:forKey:] + 292
5 UIKit 0x000000010e927994 -[UIViewController setValue:forKey:] + 87
6 JJOC 0x000000010bc0c0f6 -[JJKVCExceptionVC demoExceptionTwo] + 54
7 JJOC 0x000000010bc0c0ad -[JJKVCExceptionVC viewDidLoad] + 189
8 UIKit 0x000000010e92f01a -[UIViewController loadViewIfRequired] + 1235
9 UIKit 0x000000010e96de6c -[UINavigationController _layoutViewController:] + 56
10 UIKit 0x000000010e96e74a -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 466
11 UIKit 0x000000010e96e8bb -[UINavigationController _startTransition:fromViewController:toViewController:] + 127
12 UIKit 0x000000010e96fa03 -[UINavigationController _startDeferredTransitionIfNeeded:] + 843
13 UIKit 0x000000010e970b41 -[UINavigationController __viewWillLayoutSubviews] + 58
14 UIKit 0x000000010eb6260c -[UILayoutContainerView layoutSubviews] + 231
15 UIKit 0x000000010e84f55b -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1268
16 QuartzCore 0x000000010e3ce904 -[CALayer layoutSublayers] + 146
17 QuartzCore 0x000000010e3c2526 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 370
18 QuartzCore 0x000000010e3c23a0 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
19 QuartzCore 0x000000010e351e92 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
20 QuartzCore 0x000000010e37e130 _ZN2CA11Transaction6commitEv + 468
21 QuartzCore 0x000000010e37eb37 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 115
22 CoreFoundation 0x000000010ce7a717 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
23 CoreFoundation 0x000000010ce7a687 __CFRunLoopDoObservers + 391
24 CoreFoundation 0x000000010ce5f038 CFRunLoopRunSpecific + 440
25 UIKit 0x000000010e78608f -[UIApplication _run] + 468
26 UIKit 0x000000010e78c134 UIApplicationMain + 159
27 JJOC 0x000000010bc18c6f main + 111
28 libdyld.dylib 0x00000001110ca65d start + 1
)
這是為什么呢速蕊?因?yàn)?code>name屬性是對象娘赴,所以賦值為nil不會(huì)崩潰,對象類型可以為nil;但是age
是整數(shù)腥光,整數(shù)的類型不會(huì)是nil,這么強(qiáng)行賦值就會(huì)拋出異常出現(xiàn)錯(cuò)誤议双。如果你不小心傳了nil捉片,KVC會(huì)調(diào)用setNilValueForKey:
方法。這個(gè)方法默認(rèn)是拋出異常宗雇。
解決方法就是重寫這個(gè)方法莹规,在里面做一些額外的自定義處理。
- (void)setNilValueForKey:(NSString *)key
{
NSLog(@"屬性值不能為nil");
}
重寫了這個(gè)方法就不會(huì)崩潰和出錯(cuò)了舞虱,看輸出結(jié)果。
2017-09-09 12:05:34.857 JJOC[4849:110183] 屬性值不能為nil
2017-09-09 12:05:34.857 JJOC[4849:110183] name = 0
重寫了以后可以順利的輸出了矾兜,整形默認(rèn)值為0椅寺,所以這里age輸出值為0。
后記
未完配并,待續(xù)~~~