KVC 深刻學習和探究

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

1、成員變量,實例變量,屬性的區別

#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都是成員變量;ios

實例變量本質上就是成員變量,只是實例是針對類而言,實例是指類的聲明;git

按照上面的例子:button是實例變量 data也是實例變量,由於id是OC特有的類,本質上來講id等同於(void *);bash

實例變量的英文翻譯爲 Instance Variable (object-specific storage)app

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

除去基本數據類型int float ....等,其餘類型的變量都叫作實例變量;學習

  1. 成員變量 = 實例變量 + 基本數據類型變量,成員變量是定義在{}號中的變量,若是變量的數據類型是一個類則稱這個變量爲實例變量;
  2. 成員變量用於內部,無需與外界接觸的變量,由於成員變量不會生成set、get方法,因此外界沒法與成員變量接觸;
  3. 因爲實例變量是成員變量的一種特殊狀況,因此實例變量也是類內部使用的,無需與外部接觸的變量,這個也就是所謂的類私有變量。
  4. 根據成員變量的私有性,爲了方便訪問,因此就有了屬性變量;屬性變量是用於與其餘對象交互的變量;

屬性變量的好處就是容許讓其餘對象訪問到該變量,由於屬性建立過程當中自動產生了set方法和get方法;固然,你能夠設置只讀或者可寫等,設置方法也可自定義。因此,屬性變量是用於與其餘對象交互的變量。ui

2、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作一些特殊處理。

3、YYModel 原理分析

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

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

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

因爲本人水平有限,文中若有不足之處,望大神指出。
若是你看完後以爲對你有所幫助,勿忘點贊+關注
附本文的Demo,贈人玫瑰,手有餘香。

相關文章
相關標籤/搜索