目錄
- KVC
- 觀察者模式:KVO與通知
- 精度問題
- 三目運算符
- 點語法
1. KVC(Key-value coding)鍵值編碼臼朗。用字符串動態(tài)去操作對象
其實現(xiàn)方法是使用字符串描述要更改的對象狀態(tài)部分农猬。通過Key
名直接訪問對象的屬性,或者給對象的屬性賦值帖鸦。這樣就可以在運行時動態(tài)在訪問和修改對象的屬性抄瓦。而不是在編譯時確定。
所有的對象都可以使用KVC
鍵統(tǒng)一是字符串痕檬,而值是不支持基本數據類型的冤寿,必須將值轉換為NSNumber或者NSValue類型
操作對象的屬性和對象屬性的屬性,訪問變量的屬性,即使該屬性沒有get,set方法也可以調用
Human *human = [[Human alloc]init];
//將name屬性設置為"holydancer"
[human setValue:@"holydancer" forKey:@"name"];
//將human中的name屬性取出
NSString *nameOfHuman=[human valueForKey:@"name"];
KVC集合運算符
ProductModel *model = [[ProductModel alloc]init];
model.name = @"iMac";
model.price = @18888;
ProductModel *model1 = [[ProductModel alloc]init];
model1.name = @"iphone";
model1.price = @6999;
NSArray *array = @[model,model1];
簡單類型的集合操作符:返回 strings, numbers, dates,簡單集合操作符作用于 array 或者 set 中相對于集合操作符右側的屬性青伤。包括 @avg, @count, @max, @min, @sum.
NSString *name = [array valueForKeyPath:@"@count"];//返回集合中對象總數的 NSNumber 對象督怜。操作符右邊沒有鍵路徑。
NSNumber *price_max = [array valueForKeyPath:@"@max.price"];//比較由操作符右邊的鍵路徑指定的屬性值狠角,并返回比較結果的最大值号杠。最大值由指定的鍵路徑所指對象的 compare: 方法決定
NSString *price_min = [array valueForKeyPath:@"@min.price"];//返回的是集合中的最小值
NSNumber *price_sum = [array valueForKeyPath:@"@sum.price"];//屬性值的總和
NSNumber *price_avg = [array valueForKeyPath:@"@avg.price"];//轉換為 double, 計算其平均值,返回該平均值的 NSNumber 對象丰歌。當均值為 nil 的時候姨蟋,返回 0.
提示:你可以簡單的通過把 self 作為操作符后面的 key path 來獲取一個由 NSNumber 組成的數組或者集合的總值,例如對于數組 @[@(1), @(2), @(3)] 可使用 valueForKeyPath:@"@max.self" 來獲取最大值立帖。
NSLog(@"%@,%@,%@,%@,%@",name,price_max,price_min,price_sum,price_avg);
對象操作符眼溶,返回 NSArray 對象實例:對象操作符包括 @distinctUnionOfObjects 和 @unionOfObjects, 返回一個由操作符右邊的 key path 所指定的對象屬性組成的數組。其中 @distinctUnionOfObjects 會對數組去重晓勇,而 @unionOfObjects 不會堂飞。
NSArray *unionOfObjects = [array valueForKeyPath:@"@unionOfObjects.name"]; // 1.
NSArray *distinctUnionObjects = [array valueForKeyPath:@"@distinctUnionOfObjects.name"]; //2.
NSLog(@"%@,%@",unionOfObjects,distinctUnionObjects);
數組和集合操作符,返回的是一個 array 或者 set 對象
數組和集合操作符作用對象是嵌套的集合绑咱,也就是說绰筛,是一個集合且其內部每個元素是一個集合。數組和集合操作符包括 @distinctUnionOfArrays描融,@unionOfArrays铝噩,@distinctUnionOfSets:
@distinctUnionOfArrays / @unionOfArrays 返回一個數組,其中包含這個集合中每個數組對于這個操作符右面指定的 key path 進行操作之后的值窿克。 distinct 版本會移除重復的值骏庸。
@distinctUnionOfSets 和 @distinctUnionOfArrays 差不多, 但是它期望的是一個包含著 NSSet 對象的 NSSet ,并且會返回一個 NSSet 對象让歼。因為集合不能包含重復的值敞恋,所以它只有 distinct 操作。
NSArray *array1 = @[model,model1];
NSArray *totalArray = @[array,array1];
NSArray *distinctUnionOfArrays = [totalArray valueForKeyPath:@"@distinctUnionOfArrays.name"];
NSArray *unionOfArrays = [totalArray valueForKeyPath:@"@unionOfArrays.name"];
NSLog(@"%@,%@",distinctUnionOfArrays,unionOfArrays);
注意: 如果操作符右側 key path 指定的對象為 nil谋右,那么返回的數組中會包含 NSNull 對象.
- 四個主要方法
必須手動將值類型轉換成NSNumber
或者NSValue
類型硬猫,才能設置為value
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
valueForKey:
總是返回一個id對象,如果原本的變量類型是值類型或者結構體,返回值會封裝成NSNumber或者NSValue對象啸蜜。
- (nullable id)valueForKey:(NSString *)key;
- (nullable id)valueForKeyPath:(NSString *)keyPath;
-
嵌套數據,
KEY
為數組或者字典- 對于有序的容器坑雅,可以用下面的方法:
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
- 對于無序的容器,可以用下面的方法:
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
鍵路徑:嵌套數據中衬横,使用
KeyPath
-
在
KVC
中處理異常情況- 如果某對象有一個常用數據類型裹粤,比如
bool
,在用setvalue:
設置value
的時候蜂林,需要實現(xiàn)setNilValueForKey:(NSString *)key
- 如果某對象有一個常用數據類型裹粤,比如
在用
set value
: 設置value的時候,如果該對象不存在該屬性遥诉,比如bool
,需要實現(xiàn)- (void)setValue:(id)value forUndefinedKey:(NSString *)key
-
KVC使用場景
- 動態(tài)地取值和設值:利用KVC動態(tài)的取值和設值是最基本的用途了噪叙。
用KVC來訪問和修改私有變量:利用KVC可以隨意修改一個對象的屬性和變量(即使是私有變量)
對于類里的私有屬性矮锈,Objective-C
是無法直接訪問的,但是KVC
是可以的睁蕾。Model和字典轉換
利用KVC集合運算符苞笨,KVC可以通過運算符層次查找對象的屬性;KVC獲取值不僅可以返回一個數據子眶,還可以將某一個屬性的所有值瀑凝,數據歸類出來(B不一定是類,也可以是數組)
利用KVC可以修改系統(tǒng)的只讀變量臭杰,修改一些控件的內部屬性
這也是iOS開發(fā)中必不可少的小技巧粤咪。眾所周知很多UI控件都由很多內部UI控件組合而成的,但是Apple度沒有提供這訪問這些空間的API
硅卢,這樣我們就無法正常地訪問和修改這些控件的樣式射窒。而KVC
在大多數情況可下可以解決這個問題。最常用的就是個性化UITextField
中的placeHolderText
了将塑。
因為數據造成crash
的原因大概幾點:
- 使用字面量創(chuàng)建數組脉顿、字典,
value
為nil
- 使用KVC方法給數組字典賦值為
nil
-
null
發(fā)送方法点寥。其他的諸如null
的判斷方法及給控件賦值都不會引起crash艾疟。
2. 觀察者模式:KVO與通知
iOS的一種設計模式, 觀察者設計模式敢辩,依賴于 Objective-C 強大的 Runtime蔽莱。觀察者模式包含:
1.通知機制(notification)
2.KVO機制【可參考iOS--KVO的實現(xiàn)原理與具體應用】
- 通知機制:
委托機制是代理“一對一”的對象之間的通信,而通知機制是廣播“一對多”的對象之間的通信戚长。
//A類獲取通知中心,并發(fā)送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"statusBarHidden" object:nil userInfo:dic];
//B類注冊通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarHidden:) name:@"statusBarHidden" object:nil];
//釋放所有通知
- (void)removeObserver:(id)observer;
//釋放名稱為aName的通知
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;
- KVO
KVO提供一種機制盗冷,指定一個被觀察對象,當對象某個屬性發(fā)生更改時同廉,對象會獲得通知仪糖,并作出相應處理柑司。KVO這種編碼方式使用起來很簡單,很適用與model修改后锅劝,引發(fā)的UIVIew的變化這種情況攒驰,當更改屬性的值后,監(jiān)聽對象會立即得到通知故爵;當指定的對象的屬性被修改后,對象就會接受到通知玻粪,****前提是執(zhí)行了setter方法、或者使用了KVC賦值****诬垂。
當指定的對象的屬性被修改后劲室,則對象就會接受到通知。簡單的說就是每次指定的被觀察的對象的屬性被修改后结窘,KVO就會自動通知相應的觀察者了痹籍。
原理:
- 當一個object有觀察者時,動態(tài)創(chuàng)建這個object的類的子類
- 對于每個被觀察的property晦鞋,重寫其set方法
- 在重寫的set方法中調用- willChangeValueForKey:和- didChangeValueForKey:通知觀察者
- 當一個property沒有觀察者時,刪除重寫的方法
- 當沒有observer觀察任何一個property時棺克,刪除動態(tài)創(chuàng)建的子類
- 注冊悠垛,指定被觀察者的屬性
[objc addObserver:self forKeyPath:@"title" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
- 實現(xiàn)回調方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{}
- 移除觀察
[self removeObserver:self forKeyPath:@"title" context:nil];
- 兩者區(qū)別:
notification比KVO多了發(fā)送通知的一步。兩者都是一對多娜谊,但是對象之間直接的交互确买,notification明顯多,需要notificationCenter來做為中間交互纱皆。
notification的優(yōu)點是監(jiān)聽不局限于屬性的變化湾趾,還可以對多種多樣的狀態(tài)變化進行監(jiān)聽,監(jiān)聽范圍廣派草,例如鍵盤搀缠、前后臺等系統(tǒng)通知的使用也更顯靈活方便.
3. 精度問題
- 原因:
NSNumber
的description
方法不夠嚴謹,在調用NSNumber的description方法打印數值時近迁,會發(fā)生精度損失艺普。 - 建議 :
- 如果是double類型,處理精度有關的數據用
double
鉴竭。建議把NSNumber轉換成double再進行輸出(NSString)或計算(CGFloat)歧譬; - 有關浮點型數據,后臺傳字符串的格式搏存,防止丟失精度.
- 如果是double類型,處理精度有關的數據用
NSNumber *value=dic[@"number"];
NSLog(@"value:%@", value);
輸出:value:81.59999999999999
如果是double類型瑰步,建議把NSNumber轉換成double再進行輸出或計算 。
CGFloat numberValue = [self doubleValue];
NSString *value=[NSString stringWithFormat:@"%g",[dic[@"number"] doubleValue]];
4. 三目運算符
- 基本格式 : (關系表達式) ? 表達式1 : 表達式2;
- 執(zhí)行流程 : 關系表達式為 真 返回表達式1 關系表達式為假 返回表達式2
int num1=8,num2=3,result=0;
result= num1>num2?num1:num2;
//因為num1>num2 成立 所以最后結果為num1的值
5. 點語法
- 點語法的本質還是方法調用,當使用點語法時璧眠,編譯器會自動展開成相應的方法,而不是訪問成員變量.
- 切記點語法的本質是轉換成相應的對
setter
和getter
方法調用,如果沒有set
和get
方法,則不能使用點語法缩焦。 - 不要在
getter
與setter
方法中使用本屬性的點語法