OC學習篇之---KVC和KVO操作
一、KVC操作
OC中的KVC操作就和Java中使用反射機制去訪問類的private許可權的變數,很暴力的,這樣做就會破壞類的封裝性,本來類中的的private許可權就是不希望外界去訪問的,但是我們這樣去操作,就會反其道而行,但是我們有時候真的需要去這樣做,哎。所以說有些事不是都是順其自然的,而是需要的時候自然就誕生了。
下面就來看一下這種技術的使用:
Dog.h
Dog.m// // Dog.h // 42_KVC // // Created by jiangwei on 14-10-14. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import <Foundation/Foundation.h> @interface Dog : NSObject @end
//
// Dog.m
// 42_KVC
//
// Created by jiangwei on 14-10-14.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Dog.h"
@implementation Dog
@end
定義了Dog這個類,但是什麼都沒有,他只是一箇中間類,沒什麼作用,在這個demo中。Person.h
Person.m// // Person.h // 42_KVC // // Created by jiangwei on 14-10-14. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import <Foundation/Foundation.h> #import "Dog.h" @interface Person : NSObject{ @private NSString *_name; NSDog *_dog; NSInteger *age; } @end
//
// Person.m
// 42_KVC
//
// Created by jiangwei on 14-10-14.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Person.h"
@implementation Person
- (NSString *)description{
NSLog(@"%@",_name);
return _name;
}
@end
Person類中我們定義了兩個屬性,但是這兩個屬性對外是不可訪問的,而且也沒有對應的get/set方法。我們也實現了description方法,用於列印結果看一下測試程式碼
main.m
//
// main.m
// 42_KVC
//
// Created by jiangwei on 14-10-14.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
//KVC:很暴力,及時一個類的屬性是私有的,而且也沒有get/set方法,同樣可以讀寫
//相當於Java中的反射,破壞類的封裝性
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//設定值
//這裡setValue方法:第一個引數是value,第二個引數是key(就是類的屬性名稱)
[p setValue:@"jiangwei" forKey:@"name"];
Dog *dog = [[Dog alloc] init];
[p setValue:dog forKey:@"dog"];
//KVC設定值時,如果屬性有set方法,則優先呼叫set方法,如果沒有則直接設定上去,get方法類似
//讀取值
NSString *name = [p valueForKey:@"name"];
//設定基本資料型別
//這裡需要將基本型別轉化成NSNumber
//在設定值的時候,會有自動解包的過程,NSNumber會解包賦值給age
[p setValue:@22 forKey:@"age"];
NSLog(@"%@",p);
return 0;
}
return 0;
}
這裡我們生成一個Person物件,然後開始使用KVC技術了:1、設定屬性值
//設定值
//這裡setValue方法:第一個引數是value,第二個引數是key(就是類的屬性名稱)
[p setValue:@"jiangwei" forKey:@"name"];
Dog *dog = [[Dog alloc] init];
[p setValue:dog forKey:@"dog"];
使用setValue方法,就可以進行對屬性進行設定值操作了,同時需要傳遞這個屬性的名稱,這個和Java中使用反射機制真的很像。注:KVC設定值時,如果屬性有set方法,則優先呼叫set方法,如果沒有則直接設定上去,get方法一樣
//設定基本資料型別
//這裡需要將基本型別轉化成NSNumber
//在設定值的時候,會有自動解包的過程,NSNumber會解包賦值給age
[p setValue:@22 forKey:@"age"];
還有一個需要注意的地方:當我們在設定基本型別的時候,需要將其轉化成NSNumber型別的。2、取屬性的值
//讀取值
NSString *name = [p valueForKey:@"name"];
取值就簡單了下面再來看一下KVC中強大的功能:鍵值路徑
鍵值路徑是對於一個類中有陣列物件的屬性進行便捷操作。
看個場景:
一個作者有多本書
Author.h
//
// Author.h
// 43_KeyValuePath
//
// Created by jiangwei on 14-10-15.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Author : NSObject{
NSString *_name;
//作者出版的書,一個作者對應多個書籍物件
NSArray *_issueBook;
}
@end
作者類中定義了名字和一個書籍陣列
Author.m//
// Author.m
// 43_KeyValuePath
//
// Created by jiangwei on 14-10-15.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Author.h"
@implementation Author
@end
Book.h
//
// Book.h
// 43_KeyValuePath
//
// Created by jiangwei on 14-10-15.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Author.h"
@interface Book : NSObject{
Author *_author;
}
@property NSString *name;
@property float *price;
@end
定義了一個作者屬性,書的名字,價格Book.m
//
// Book.m
// 43_KeyValuePath
//
// Created by jiangwei on 14-10-15.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Book.h"
@implementation Book
@end
看一下測試程式碼
main.m
//
// main.m
// 43_KeyValuePath
//
// Created by jiangwei on 14-10-15.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Book.h"
#import "Author.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//------------------KVC鍵值路徑
/*
Book *book = [[Book alloc] init];
Author *author = [[Author alloc] init];
//設定作者
[book setValue:author forKey:@"author"];
//設定作者的名字
//路徑為:author.name,中間用點號進行連線
[book setValue:@"jiangwei" forKeyPath:@"author.name"];
NSString *name = [author valueForKey:@"name"];
NSLog(@"name is %@",name);
*/
//--------------------KVC的運算
Author *author = [[Author alloc] init];
[author setValue:@"莫言" forKeyPath:@"name"];
Book *book1 = [[Book alloc] init];
book1.name = @"紅高粱";
book1.price = 9;
Book *book2 = [[Book alloc] init];
book2.name = @"蛙";
book2.price = 10;
NSArray *array = [NSArray arrayWithObjects:book1,book2, nil];
[author setValue:array forKeyPath:@"issueBook"];
//基本資料型別會自動被包裝成NSNumber,裝到陣列中
//得到所有書籍的價格
NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];
NSLog(@"%@",priceArray);
//獲取陣列的大小
NSNumber *count = [author valueForKeyPath:@"[email protected]"];
NSLog(@"count=%@",count);
//獲取書籍價格的總和
NSNumber *sum = [author valueForKeyPath:@"[email protected]"];
NSLog(@"%@",sum);
//獲取書籍的平均值
NSNumber *avg = [author valueForKeyPath:@"[email protected]"];
NSLog(@"%@",avg);
//獲取書籍的價格最大值和最小值
NSNumber *max = [author valueForKeyPath:@"[email protected]"];
NSNumber *min = [author valueForKeyPath:@"[email protected]"];
}
return 0;
}
1、首先通過前面說到的KVC設定作者的書籍陣列//--------------------KVC的運算
Author *author = [[Author alloc] init];
[author setValue:@"莫言" forKeyPath:@"name"];
Book *book1 = [[Book alloc] init];
book1.name = @"紅高粱";
book1.price = 9;
Book *book2 = [[Book alloc] init];
book2.name = @"蛙";
book2.price = 10;
NSArray *array = [NSArray arrayWithObjects:book1,book2, nil];
[author setValue:array forKeyPath:@"issueBook"];
添加了兩本書籍2、下面就開始用到KVC中鍵值路徑了
1)獲取作者類中書籍陣列中所有書籍的價格
//基本資料型別會自動被包裝成NSNumber,裝到陣列中
//得到所有書籍的價格
NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];
NSLog(@"%@",priceArray);
看到了:@"issueBook.price" 這就是鍵值路徑的使用,issueBook是作者類中的書籍陣列屬性名,price是書籍類的屬性,中間用點號進行連線,這樣我們就可以獲取到了所有書籍的價格了,如果在Java中,我們需要用一個迴圈操作。但是OC中多麼方便。2)獲取作者類中書籍陣列的大小
//獲取陣列的大小
NSNumber *count = [author valueForKeyPath:@"[email protected]"];
NSLog(@"count=%@",count);
使用 @"[email protected]" 鍵值路徑獲取書籍陣列的大小,issueBook是作者類中的書籍陣列屬性名,@count是特定一個寫法,可以把它想象成一個方法,中間任然用點號進行連線3)獲取作者類中書籍陣列的價格總和
//獲取書籍價格的總和
NSNumber *sum = [author valueForKeyPath:@"[email protected]"];
NSLog(@"%@",sum);
使用 @"[email protected]" 鍵值路徑獲取書籍陣列中的價格總和,issueBook是作者類中的書籍陣列屬性名,@sum是特性寫法,可以把它想象成一個方法,price是書籍的價格屬性名,可以把它看成是@sum的一個引數,中間用點號進行連線如果在java中,這個需要用一個迴圈來計算總和,OC中很方便的
4)獲取作者類中書籍陣列的價格平均值、最小值、最大值
//獲取書籍的平均值
NSNumber *avg = [author valueForKeyPath:@"[email protected]"];
NSLog(@"%@",avg);
//獲取書籍的價格最大值和最小值
NSNumber *max = [author valueForKeyPath:@"[email protected]"];
NSNumber *min = [author valueForKeyPath:@"[email protected]"];
操作和上面類似,這裡就不解釋了
我們看到上面返回來的資料都是NSNumber型別的
二、KVO操作
KVO操作在OC中也是經常會用到的,而且這種機制在java中不存在的。
它的作用就是用來監聽類中屬性值的變化,實現原理是觀察者模式,當然我們也可以使用觀察者模式在Java中實現這樣的機制
看一下具體的例子:現在有一個小孩類,他有兩個屬性:開心值,飢餓值,然後還有一個護士類,用來監聽孩子類的這兩個屬性值的
Chidren.h
//
// Children.h
// 44_KVO
//
// Created by jiangwei on 14-10-16.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Children : NSObject
@property NSInteger *hapyValue;
@property NSInteger *hurryValue;
@end
Children.m
//
// Children.m
// 44_KVO
//
// Created by jiangwei on 14-10-16.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Children.h"
@implementation Children
- (id) init{
self = [super init];
if(self != nil){
//啟動定時器
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
self.hapyValue= 100;
}
return self;
}
- (void) timerAction:(NSTimer *) timer{
//使用set方法修改屬性值,才能觸發KVO
int value = _hapyValue;
[self setHapyValue:--value];
int values = _hurryValue;
[self setHurryValue:--values];
}
@end
在初始化方法中,我們啟動一個定時器,然後隔1s就去修改孩子類的值Nure.h
//
// Nure.h
// 44_KVO
//
// Created by jiangwei on 14-10-16.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import <Foundation/Foundation.h>
@class Children;
@interface Nure : NSObject{
Children *_children;
}
- (id) initWithChildren:(Children *)children;
@end
定義一個孩子屬性Nure.m
//
// Nure.m
// 44_KVO
//
// Created by jiangwei on 14-10-16.
// Copyright (c) 2014年 jiangwei. All rights reserved.
//
#import "Nure.h"
#import "Children.h"
@implementation Nure
- (id) initWithChildren:(Children *)children{
self = [super init];
if(self != nil){
_children = children;
//觀察小孩的hapyValue
//使用KVO為_children物件新增一個觀察者,用於觀察監聽hapyValue屬性值是否被修改
[_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
//觀察小孩的hurryValue
[_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
}
return self;
}
//觸發方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"%@",change);
//通過列印change,我們可以看到對應的key
//通過keyPath來判斷不同屬性的觀察者
if([keyPath isEqualToString:@"hapyValue"]){
//這裡change中有old和new的值是因為我們在呼叫addObserver方法時,用到了
//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個
//[change objectForKey:@"old"]是修改前的值
NSNumber *hapyValue = [change objectForKey:@"new"];//修改之後的最新值
NSInteger *value = [hapyValue integerValue];
if(value < 90){
//do something...
}
}else if([keyPath isEqualToString:@"hurryValue"]){
//這裡change中有old和new的值是因為我們在呼叫addObserver方法時,用到了
//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個
//[change objectForKey:@"old"]是修改前的值
NSNumber *hurryValue = [change objectForKey:@"new"];//修改之後的最新值
NSInteger *value = [hurryValue integerValue];
if(value < 90){
//do something...
}
}
NSLog(@"%@",context);//列印的就是addObserver方法的context引數
//使用KVC去修改屬性的值,也會觸發事件
}
- (void)dealloc{
//移除觀察者
[_children removeObserver:self forKeyPath:@"hapyValue"];
[_children removeObserver:self forKeyPath:@"hurryValue"];
}
@end
看到了在這裡就開始進行監聽操作了下面來具體看一下如何做到監聽的
1、新增監聽物件
我們使用addObserver方法給孩子新增監聽物件
第一個引數:監聽者,這裡是Nure,所以可以直接傳遞self
第二個引數:監聽物件的屬性名
第三個引數:監聽這個屬性的狀態:這裡可以使用|進行多種組合操作,屬性的新值和舊值
第四個引數:傳遞內容給監聽方法
//觀察小孩的hapyValue
//使用KVO為_children物件新增一個觀察者,用於觀察監聽hapyValue屬性值是否被修改
[_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
//觀察小孩的hurryValue
[_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
2、監聽方法
//觸發方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"%@",change);
//通過列印change,我們可以看到對應的key
//通過keyPath來判斷不同屬性的觀察者
if([keyPath isEqualToString:@"hapyValue"]){
//這裡change中有old和new的值是因為我們在呼叫addObserver方法時,用到了
//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個
//[change objectForKey:@"old"]是修改前的值
NSNumber *hapyValue = [change objectForKey:@"new"];//修改之後的最新值
NSInteger *value = [hapyValue integerValue];
if(value < 90){
//do something...
}
}else if([keyPath isEqualToString:@"hurryValue"]){
//這裡change中有old和new的值是因為我們在呼叫addObserver方法時,用到了
//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個
//[change objectForKey:@"old"]是修改前的值
NSNumber *hurryValue = [change objectForKey:@"new"];//修改之後的最新值
NSInteger *value = [hurryValue integerValue];
if(value < 90){
//do something...
}
}
NSLog(@"%@",context);//列印的就是addObserver方法的context引數
//使用KVC去修改屬性的值,也會觸發事件
}
我們上面傳遞的第一個引數是監聽者,這個方法也是在監聽者中實現的,當屬性值發生變化的時候,這個方法會被回撥這個方法的引數:
第一個引數:鍵值路徑
第二個引數:監聽物件
第三個引數:變化的值
第四個引數:傳遞的內容
我們看到程式碼中有一個特殊的引數:第三個引數:NSDirctionary型別的
其實我們如果不知道是幹什麼的,我們可以列印一下他的結果看一下,很簡單,這裡就不截圖說明了
我們會發現他有兩個鍵值對
key是:new和old
他們就是分別代表這個屬性值變化的前後值,同時他們的得到也和之前我們新增監聽物件時設定的第三個引數有關:
NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld
那個地方設定了幾種狀態,這裡的NSDirctionary中就會有幾個鍵值對3、銷燬方法
這個並不屬於KVO的內容了,只是在這裡用到了就順便說一下
- (void)dealloc{
//移除觀察者
[_children removeObserver:self forKeyPath:@"hapyValue"];
[_children removeObserver:self forKeyPath:@"hurryValue"];
}
我們在建立一個物件的時候會呼叫alloc方法,當物件被銷燬的時候會呼叫dealloc這個方法,這個和C++中的解構函式一樣,Java中有垃圾回收器,所以沒有此類的方法,但是有一個finalize方法,其實這個方法就是在垃圾回收器回收物件的時候會呼叫,和這個功能差不多,但是在Java中,我們並不提倡使用這個方法。因為會造成GC的回收發生錯誤。我們在銷燬方法中需要移除監聽者
總結
這一篇就介紹了OC中比較有特色的兩個機制:KVC和KVO
KVC:就是可以暴力的去get/set類的私有屬性,同時還有強大的鍵值路徑對陣列型別的屬性進行操作
KVO:監聽類中屬性值變化的
相關推薦
OC學習篇之---KVC和KVO操作
一、KVC操作OC中的KVC操作就和Java中使用反射機制去訪問類的private許可權的變數,很暴力的,這樣做就會破壞類的封裝性,本來類中的的private許可權就是不希望外界去訪問的,但是我們這樣去操作,就會反其道而行,但是我們有時候真的需要去這樣做,哎。所以說有些事不是
OC學習篇之--- property和 synthesize的使用
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---檔案的操作
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---協議的概念和用法
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---類的初始化方法和點語法的使用
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---Foundation框架中的NSArray類和NSMutableArray類
在之前的一篇文章中介紹了Foundation框架中的NSString類和NSMutableString類:今天我們繼續來看一下Foundation框架中的NSArray類和NSMutableArray類,其實NSArray類和Java中的List差不多,算是一種資料結構,當然
OC學習篇之---謂詞 NSPredicate
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---類的三大特性 封裝,繼承,多型
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---通知 NSNotificationCenter
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OC學習篇之---歸檔和解擋
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
資料結構學習篇之棧和佇列
棧和佇列是什麼 棧和佇列是兩種特殊的線性表,它們是限定只能在表的一端或兩端進行插入、刪除元素的線性表,因此,統稱為限定性資料結構。 共同點: 都是隻能線上性表的端點插入和刪除。不同點: 棧的插入和刪除都線上性表的同一個端點,該點通稱棧頂,相應地,不能插入刪除的另一個端點通稱棧底,其特性是後進先出。
OC學習篇之---類的三大特性(封裝,繼承,多型)
之前的一片文章介紹了OC中類的初始化方法和點語法的使用:http://blog.csdn.net/jiangwei0910410003/article/details/41683873,今天來繼續學習OC中的類的三大特性,我們在學習Java的時候都知道,類有三大特性:繼承,封
C#學習篇之基礎回顧(16)----- 程序和多執行緒
程序 程序(Process)是Windows系統的一個基本概念。一個應用程式在作業系統中執行被視為一個程序,程序可以包括一個或多個執行緒。 程序之間是相對獨立的,一個程序無法訪問另一個程序的資料(除非使用分散式計算方式),一個程序執行失敗也不會影響另一個程序。 執行緒
JavaWeb學習篇之----容器Response詳解(有關response setCharacterEncoding和setContentType)詳細對比說明
今天在來看一下Response容器的相關知識,其實這篇blog早就應該編寫了,只是最近有點忙,所以被中斷了。下面我們就來看一下Response容器的相關知識吧。Response和我們即將在後面說到的Request容器是一一對應的,他是web容器在使用者每次請求服務端的
JavaWeb學習篇之----Servlet過濾器Filter和監聽器
*************************************************************************************** Servlet過濾器是在Java Servlet規範2.3中定義的,它能夠對Servlet容器的請求和響應物件進行檢查和修改。
OC學習小結之ios運行過程詳解
for cat 用戶 with res nbsp c學習 launch cati 1)ios核心類 UIView 視圖,屏幕上能看得見的東西都是視圖,例如:按鈕、文本標簽、和表格等 UIViewController:內部默認有個視圖(UIView),負責管理UIView的
MongoDB 學習筆記之 分片和副本集混合運用
comment ssm table mmap insert ise class 學習 urn 分片和副本集混合運用: 基本架構圖: 搭建詳細配置: 3個shard + 3個replicat set + 3個configserver + 3個Mongos sh
IDEA 學習筆記之 安裝和基本配置
window eclipse 自動 ref size 工作 ips ctr line 安裝和基本配置: 下載:https://www.jetbrains.com/idea/download/#section=windows 下載Zip安裝包: 基礎知識:
Java 學習筆記之 Error和Exception的聯系
相關 runt ble 及其 捕獲 exce throw 繼承 編譯 Error和Exception的聯系: Error和Exception的聯系 繼承結構:Error和Exception都是繼承於Throwable,RuntimeException繼承自Excep
Python網絡編程篇之select和epoll
unix cat 必須 inpu 結束 新的 eno {} 提升 1. select 原理 在多路復?的模型中, ?較常?的有select模型和epoll模型。 這兩個都是系統接?, 由操作系統提供。 當然, Python的select模塊進?了更?級的封裝。 ?絡通信被U