1. 程式人生 > Android開發 >KVC 深入學習和探究

KVC 深入學習和探究

直入主題,開頭先介紹下本篇學習和探究方向,首先搞清楚成員變數、例項變數、屬性的定義,以便KVC賦值取值時能夠輕鬆區分;其次深入探究KVC取值原理、賦值原理;再次通過對YYmodel原始碼的分析,深入理解KVC;最後再探究一下Category的實現原理及其使用時的注意點。

一、成員變數,例項變數,屬性的區別

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
//{}內的全部為成員變數,例項變數是一種特殊的成員變數,是由OC中的類宣告的成員變數
{
    UIButton *button;
    int count;
    id data;
}
//GCC --> LLVM 屬性
//1.預設的setter + getter
//2.自動建立一個帶下劃線的例項變數匹配屬性
@property (nonatomic,strong) UIButton *myButton;
@end

@implementation ViewController
@synthesize  myButton = _myButton; //指定與屬性對應的例項變數
編譯器期間,讓編譯器自動生成getter/setter方法。
當有自定義的存或取方法時,自定義會遮蔽自動生成該方法

@dynamic  myButton;
告訴編譯器,不自動生成屬性對應的例項變數和訪問方法(getter/setter),避免編譯期間產生警告;
然後由自己實現存取方法或存取方法在執行時動態建立繫結

@end
複製程式碼

在{ }中宣告的變數都是成員變數;按照上面的例子:button count data都是成員變數;

例項變數本質上就是成員變數,只是例項是針對類而言,例項是指類的宣告;

按照上面的例子:button是例項變數 data也是例項變數,因為id是OC特有的類,本質上來說id等同於(void *);

例項變數的英文翻譯為 Instance Variable (object-specific storage)

例項的英文翻譯為 Instance (manifestation of a class)說的是"類的表現",說明例項變數應該是由類定義的變數!

除去基本資料型別int float ....等,其他型別的變數都叫做例項變數;

  1. 成員變數 = 例項變數 + 基本資料型別變數,成員變數是定義在{}號中的變數,如果變數的資料型別是一個類則稱這個變數為例項變數;
  2. 成員變數用於內部,無需與外界接觸的變數,因為成員變數不會生成set、get方法,所以外界無法與成員變數接觸;
  3. 由於例項變數是成員變數的一種特殊情況,所以例項變數也是類內部使用的,無需與外部接觸的變數,這個也就是所謂的類私有變數。
  4. 根據成員變數的私有性,為了方便訪問,所以就有了屬性變數;屬性變數是用於與其他物件互動的變數;

屬性變數的好處就是允許讓其他物件訪問到該變數,因為屬性建立過程中自動產生了set方法和get方法;當然,你可以設定只讀或者可寫等,設定方法也可自定義。所以,屬性變數是用於與其他物件互動的變數。

二、Key-Value Coding 鍵值編碼機制

鍵值編碼是一種機制,通過NSKeyValueCoding非正式協議,物件採用這種機制提供對其屬性的間接訪問。當物件符合鍵值編碼時,它的屬性可以使用字串引數通過簡明、統一的訊息介面進行定址。這種間接訪問機制補充了例項變數及其關聯的訪問器方法提供的直接訪問。 Key-Value Coding Programming Guide 官方檔案

  1. 賦值過程--- setValue:forKey:的預設實現及驗證
    KVC賦值原理圖
    • 1.按照順序查詢第一個名為set<Key>:_set<Key>:的方法。如果找到,傳入value值並呼叫。
      • 1.1 同時寫了set<Key>:``_set<Key>:兩個方法,優先呼叫set<Key>:方法;
      • 1.2 註釋掉set<Key>:方法後,就呼叫了_set<Key>:方法,證實了KVC的前期賦值情況!
    • 2.如果找不到簡單訪問器,並且類方法accessInstanceVariablesDirectly返回YES,則按以下順序查詢例項變數:_<key>_is<Key><key>is<Key> 。如果找到,則直接使用value值設定變數並完成。
      • 2.1如果兩個方法都沒有實現,此時KVC會看accessInstanceVariablesDirectly方法,返回Yes代表可以直接訪問成員變數,反之不能訪問成員變數!如果返回為Yes,會按照_key、_isKey、key、isKey成員屬性進行賦值,當四個成員變數同時出現,首先給_key賦值;
      • 2.2 將_key成員變數註釋掉後,就優先給_isKey賦值,優先順序僅次於_key;
      • 2.3 將_isKey註釋掉之後,發現給key賦值;
      • 2.4 將key成員變數註釋掉之後,最後給isAge賦值,符合了上述setValue:forkey的訪問成員變數的優先順序 _key > _isKey > key > isKey的順序。
    • 3.如果找不到以上方法或例項變數,則呼叫setValue:forUndefinedKey:。預設情況下,這會引發異常,但NSObject的子類可以通過過載並根據特定key做一些特殊處理。
  2. 取值過程--- valueForKey:的預設實現及驗證
    KVC取值原理圖
    • 1.按照順序搜尋名稱為get<Key><key>is<Key>_<key>的第一個訪問器方法。如果找到,呼叫它並繼續到步驟3。否則請繼續執行下一步。
      • ViewController 中的程式碼如下:
      @implementation ViewController
      - (void)viewDidLoad {
          [super viewDidLoad];
          _p = [[YNPerson alloc]init];
          NSLog(@"%@",[_p valueForKey:@"name"]);
      }
      @end
      複製程式碼
      • 1.1 當有四個方法時,會優先呼叫get<Key>方法;
      • 1.2 將get<Key>方法註釋掉後,呼叫了<key>方法,驗證了get<Key>><key>
      • 1.3 將get<Key><key>方法註釋掉後,呼叫了is<Key>方法,驗證了get<Key>> <key> > is<Key>;
      • 1.4 將get<Key><key>方法以及is<Key>註釋掉後,呼叫了_<key>方法,驗證了get<Key> > <key> > is<Key> > _<key>;
    • 2.如果找不到簡單訪問器方法,並且訊息接收者的類方法accessInstanceVariablesDirectly返回YES,則系統按以下順序搜尋名為:_<key>_is<Key><key>is<Key>的例項變數。如果找到,則直接獲取例項變數的值,然後繼續執行步驟3。否則,繼續跳轉到步驟4。
      • YNPerson.h中新增四個成員變數:
      @interface YNPerson : NSObject
      {
          @public 
          NSString *_name;
          NSString *_isName;
          NSString *name;
          NSString *isName;
      }
      @end
      複製程式碼
      • ViewController 中的呼叫程式碼如下:
      @implementation ViewController
      - (void)viewDidLoad {
          [super viewDidLoad];
          _p = [[YNPerson alloc]init];
          _p->_name   = @"_name";
          _p->_isName = @"_isName";
          _p->name    = @"name";
          _p->isName  = @"isName";
          NSLog(@"%@",[_p valueForKey:@"name"]);
      }
      @end
      複製程式碼
      • 2.1 四個成員變數都存在,優先取_<key>成員變數的值;
      • 2.2 沒有_<key>成員變數時(註釋掉成員變數宣告和賦值),取_is<Key>的值;
      • 2.3 沒有_<key>_is<Key>成員變數時(註釋掉成員變數宣告和賦值),取<key>的值;
      • 2.4 沒有_<key>_is<Key><key>成員變數時(註釋掉成員變數宣告和賦值),取is<Key>的值;
    • 3.如果獲取到的屬性值是物件指標,即獲取的是物件,則直接將物件返回。如果獲取到的屬性值是NSNumber支援的資料型別,則將其儲存在NSNumber例項並返回。如果獲取到的屬性值不是 NSNumber 支援的型別,則轉換為NSValue物件,然後返回。
    • 4.如果上述所有方法都沒有執行,則呼叫valueForUndefinedKey:。預設情況下,這會引發異常,但NSObject的子類可以通過過載並根據特定key做一些特殊處理。

三、YYModel 原理分析

關於此部分內容,請移步至YYModel 原理分析,歡迎查閱、指正。

四、Category的實現原理及其使用

關於此部分內容,請移步至Category的實現原理及其使用,歡迎查閱、指正。

由於本人水平有限,文中如有不足之處,望大神指出。
如果你看完後覺得對你有所幫助,勿忘點贊+關注
附本文的Demo,贈人玫瑰,手有餘香。