簡介 KVC(Key-value coding)鍵值編碼,顧名思義。額,簡單來說,是可以通過對象屬性名稱(Key)直接給屬性值(value)編碼(coding)“編碼”可以理解為“賦值”。這樣可以免去我們調用getter和setter方法,從而簡化我們的代碼,也可以用來修改系統控制項內部屬性(這個黑魔 ...
簡介
KVC(Key-value coding)鍵值編碼,顧名思義。額,簡單來說,是可以通過對象屬性名稱(Key)直接給屬性值(value)編碼(coding)“編碼”可以理解為“賦值”
。這樣可以免去我們調用getter和setter方法,從而簡化我們的代碼,也可以用來修改系統控制項內部屬性(這個黑魔法且用且珍惜
)。
1. 最簡單的使用例子
- 假設有
CYXModel
類與CYXShopModel
類,CYXModel
裡面有name
、product
屬性,CYXShopModel
裡面有productName
屬性。
@interface CYXModel: NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) CYXShopModel *product;
@end
@interface CYXShopModel: NSObject
@property (nonatomic, strong) NSString * productName;
@end
- 不使用KVC,我們這樣訪問
CYXModel
的屬性- 取值:
CYXModel *model = [[CYXModel alloc]init];
NSString *name = model. name;
CYXShopModel *shop = model. product;
NSString *productName = shop. productName;
- 設值:
CYXModel *model = [[CYXModel alloc]init];
model. name = @"CYX";
CYXShopModel *shopModel = [[CYXShopModel alloc]init];
shopModel. productName = @"NIKE";
model. product = shopModel;
- 使用KVC,我們可以這樣訪問
CYXModel
的屬性 - 取值:
CYXModel *model = [[CYXModel alloc]init];
NSString *name = [model valueForKey: @"name" ];
NSString *productName = [model valueForKeyPath: @"product.productName" ];
- 設值:
CYXModel *model = [[CYXModel alloc]init];
[model setValue:@"CYX" forKey:@"name"];
[model setValue:@"NIKE" forKeyPath:@"product.productName"];
註: 這個簡單的例子,可能你看了覺得這並沒什麼卵用,下麵我們來分析一下稍微有點卵用的例子吧。
2. KVC字典轉模型的實現原理
-
假設dict字典中有name,icon的Key,CYXModel模型類中必須要有同名的name,icon屬性與之相對應。
-
我們使用
[CYXModel setValuesForKeysWithDictionary:dict];
進行字典轉模型。 -
setValuesForKeysWithDictionary:
方法內部實現原理如下:- (1) 遍歷字典裡面所有的key和值,name,icon。
// enumerateKeysAndObjectsUsingBlock:遍歷字典中的所有keys和valus [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { // 利用KVC給模型中屬性賦值,, // key:用來給哪個屬性 // Value:給模型的值 [CYXModel setValue:obj forKey:key]; }];
- (2) 分別給屬性賦值
[CYXModel setValue:dict[@"name"] forKey:@"name"];
[CYXModel setValue:dict[@"icon"] forKey:@"icon"];
-
setValue:forKey:
方法:給模型的屬性賦值- 賦值原理:
- (1)去模型中查找有沒有
setIcon
方法,就直接調用這個set方法,給模型這個屬性賦值[self setIcon:dict[@"icon"]];
- (2)如果找不到set方法,接著就會去尋找有沒有
icon
屬性,如果有,就直接訪問模型中icon = dict[@"icon"];
- (3)如果找不到icon屬性,接著又會去尋找
_icon
屬性,如果有,直接_icon = dict[@"icon"];
- (4)如果都找不到就會報錯
[<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]
- (1)去模型中查找有沒有
- 賦值原理:
-
擴展:讀者可以去查查KVV(鍵值驗證),進一步理解報錯原因與容錯方法。
作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群:519832104 不管你是小白還是大牛歡迎入駐,分享經驗,討論技術,大家一起交流學習成長!
另附上一份各好友收集的大廠面試題,需要iOS開發學習資料、面試真題,可以添加iOS開發進階交流群,進群可自行下載!
3. 修改系統控制項內部屬性(runtime + KVC)
-
有時候,UI會閑著沒事,會給你找點事情,例如,界面設計圖是這樣的:
-
這。。怎麼感覺有點不同,這
UIPageControl
怎麼跟我平常用的不一樣?平常不都是這樣的??如下圖 -
首先想到的肯定是,查看
UIPageControl
的頭文件,如下:
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIPageControl : UIControl
@property(nonatomic) NSInteger numberOfPages; // default is 0
@property(nonatomic) NSInteger currentPage; // default is 0\. value pinned to 0..numberOfPages-1
@property(nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO
@property(nonatomic) BOOL defersCurrentPageDisplay; // if set, clicking to a new page won't update the currently displayed page until -updateCurrentPageDisplay is called. default is NO
- (void)updateCurrentPageDisplay; // update page display to match the currentPage. ignored if defersCurrentPageDisplay is NO. setting the page value directly will update immediately
- (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; // returns minimum size required to display dots for given page count. can be used to size control if page count could change
@property(nullable, nonatomic,strong) UIColor *pageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;
@property(nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;
@end
- 卧槽,就這麼幾個屬性可以給我設的,不夠用啊兄弟。能不能給我個可以賦值
UIImage
對象的屬性?看來正常途徑使用系統的控制項是設不了了,剩下的我感覺只有兩種方法(如有其它,歡迎指出),一種是自定義PageControl
,這種方式看起來不簡單,各位有興趣可以去試試。另一種方式就是,通過runtime遍歷出UIPageControl
所有屬性(包括私有成員屬性,runtime確實很強大)。 - 使用runtime遍歷
UIPageControl
結果(下篇文字再談談runtime,這裡暫不解釋)如下列印:
2016-03-23 01:09:26.161 TenMinDemo[6224:507269] UIPageControl -> _lastUserInterfaceIdiom = q
2016-03-23 01:09:26.161 TenMinDemo[6224:507269] UIPageControl -> _indicators = @"NSMutableArray"
2016-03-23 01:09:26.161 TenMinDemo[6224:507269] UIPageControl -> _currentPage = q
2016-03-23 01:09:26.161 TenMinDemo[6224:507269] UIPageControl -> _displayedPage = q
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _pageControlFlags = {?="hideForSinglePage"b1"defersCurrentPageDisplay"b1}
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _currentPageImage = @"UIImage" // 當前選中圖片
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _pageImage = @"UIImage" // 預設圖片
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _currentPageImages = @"NSMutableArray"
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _pageImages = @"NSMutableArray"
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _backgroundVisualEffectView = @"UIVisualEffectView"
2016-03-23 01:09:26.162 TenMinDemo[6224:507269] UIPageControl -> _currentPageIndicatorTintColor = @"UIColor"
2016-03-23 01:09:26.163 TenMinDemo[6224:507269] UIPageControl -> _pageIndicatorTintColor = @"UIColor"
2016-03-23 01:09:26.163 TenMinDemo[6224:507269] UIPageControl -> _legibilitySettings = @"_UILegibilitySettings"
2016-03-23 01:09:26.163 TenMinDemo[6224:507269] UIPageControl -> _numberOfPages = q
- 結果非常滿意,果然找到我想要的圖片設置屬性。
- 然後通過KVC設置自定義圖片,實現了效果,代碼如下:
UIPageControl *pageControl = [[UIPageControl alloc] init];
[pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"];
[pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];
- 註:
這裡只是拋磚引玉的講了個小例子,其他的神奇功能等待讀者去發現啦。
提示: 在xib/Storyboard中,也可以使用KVC,下麵是在xib中使用KVC把圖片邊框設置成圓角