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

来源:https://www.cnblogs.com/chengxyyh/archive/2020/06/22/13177032.html
-Advertisement-
Play Games

簡介 KVC(Key-value coding)鍵值編碼,顧名思義。額,簡單來說,是可以通過對象屬性名稱(Key)直接給屬性值(value)編碼(coding)“編碼”可以理解為“賦值”。這樣可以免去我們調用getter和setter方法,從而簡化我們的代碼,也可以用來修改系統控制項內部屬性(這個黑魔 ...


簡介

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(鍵值驗證),進一步理解報錯原因與容錯方法。

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的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把圖片邊框設置成圓角


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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近做的項目,有個需求(從Elastic Search取數據,業務運算後),每次要向MySQL插入1300萬條數據左右。最初用MySQL的executemany()一次插入10000條數據,統計的時間如下: 如上,插入時間由於系統的IO變化,會有波動,最快在4秒左右。 後改為"load data i ...
  • 一、Kafka持久化概述 Kakfa 依賴文件系統來存儲和緩存消息。對於硬碟的傳統觀念是硬碟總是很慢,基於文件系統的架構能否提供優異的性能?實際上硬碟的快慢完全取決於使用方式。同時 Kafka 基於 JVM 記憶體有以下缺點: 對象的記憶體開銷非常高,通常是要存儲的數據的兩倍甚至更高 隨著堆內數據的增加 ...
  • 基礎環境 準備3台虛擬機 配置無密碼登錄 配置方法:https://ipooli.com/2020/04/linux_host/ 並且做好主機映射。 下載Flink https://www.apache.org/dyn/closer.lua/flink/flink-1.10.1/flink-1.10 ...
  • 1. redis-cli命令行遠程連接redis服務 redis-cli -h host -p port -a password host:遠程redis伺服器host port:遠程redis服務埠 password:遠程redis服務密碼 如:下圖所示,redis-cli -h 172.16. ...
  • 1.對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。2.應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。3.應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進 ...
  • /* * This file is part of the SDWebImage package. * (c) Olivier Poitrey * * For the full copyright and license information, please view the LICENSE *  ...
  • Flutter給我們提供了很多而且很好用的內置動畫,這些動畫僅僅需要簡單的幾行代碼就可以實現一些不錯的效果,Flutter的動畫分為補間動畫和基於物理的動畫,基於物理的動畫我們先不說。 補間動畫很簡單,Android裡面也有補間動畫,就是給UI設置初始的狀態和結束狀態,經過我們定義的一段時間,系統去... ...
  • 上篇: 跳槽季“iOS開發”救救自己,別再這樣寫簡歷了 簡歷中需要註意的問題!! HR每天要收到500+簡歷還不止,首先就是簡歷的過濾。就相當於翻牌子。廢話不多說下麵講重點: 簡歷拼寫錯誤:(❌)單詞拼接錯了就不提了,直接pass, 好感度馬上降為零。 比如:githup/CNDS/Foudatio ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...