iOS底層原理——KVC使用實踐以及實現

簡介

KVC(Key-value coding)鍵值編碼,顧名思義。額,簡單來講,是能夠經過對象屬性名稱(Key)直接給屬性值(value)編碼(coding)「編碼」能夠理解爲「賦值」。這樣能夠免去咱們調用getter和setter方法,從而簡化咱們的代碼,也能夠用來修改系統控件內部屬性(這個黑魔法且用且珍惜)。面試

1. 最簡單的使用例子

  • 假設有CYXModel類與CYXShopModel類,CYXModel裏面有nameproduct屬性,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"];

注: 這個簡單的例子,可能你看了以爲這並沒什麼卵用,下面咱們來分析一下稍微有點卵用的例子吧。ide

2. KVC字典轉模型的實現原理

  • 假設dict字典中有name,icon的Key,CYXModel模型類中必需要有同名的name,icon屬性與之相對應。學習

  • 咱們使用[CYXModel setValuesForKeysWithDictionary:dict];進行字典轉模型。ui

  • 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:方法:給模型的屬性賦值atom

    • 賦值原理:
      • (1)去模型中查找有沒有setIcon方法,就直接調用這個set方法,給模型這個屬性賦值[self setIcon:dict[@"icon"]];
      • (2)若是找不到set方法,接着就會去尋找有沒有icon屬性,若是有,就直接訪問模型中icon = dict[@"icon"];
      • (3)若是找不到icon屬性,接着又會去尋找_icon屬性,若是有,直接_icon = dict[@"icon"];
      • (4)若是都找不到就會報錯
        [<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]
  • 擴展:讀者能夠去查查KVV(鍵值驗證),進一步理解報錯緣由與容錯方法。設計

做爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個個人iOS交流羣:519832104 無論你是小白仍是大牛歡迎入駐,分享經驗,討論技術,你們一塊兒交流學習成長!code

另附上一份各好友收集的大廠面試題,須要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把圖片邊框設置成圓角


點擊此處,當即與iOS大牛交流學習

相關文章
相關標籤/搜索