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"];

注: 這個簡單的例子,可能你看了覺得這并沒什么卵用,下面我們來分析一下稍微有點卵用的例子吧。

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:]
  • 擴展:讀者可以去查查KVV(鍵值驗證),進一步理解報錯原因與容錯方法。


注: 稍微有點卵用的看完,接下來說一個比較有卵用的用法,這個例子需要配合runtime來實現,有興趣可以看看,runtime內容不少,這里就暫不介紹了,歡迎 關注,在下篇文字小結一下runtime。

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把圖片邊框設置成圓角

xib中設置KVC

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • 一、KVC的概念理解及常用方法 概念 KVC(Key-Value Coding)顧名思義,就是鍵值編碼的意思。在i...
    RM_乾笙閱讀 3,654評論 0 10
  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iO...
    朽木自雕也閱讀 1,571評論 6 1
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評論 0 9
  • 對于從事 iOS 開發人員來說,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,730評論 7 64
  • 曾經,我心目中的愛情,就算不是轟轟烈烈,至少也要矢志不渝。 早在豆蔻之年,雖不懂什么男女情愛,卻也知道自己喜歡和哪...
    攸水浮萍閱讀 237評論 3 2