理解類與對象的本質對於掌握一門語言是相當重要的,本文將從結構類型的角度探討OC的類對象、實例對象、元類對象(Meta Class)。css
咱們先看一張圖:git
- 每一個 Class 都有一個 isa 指針指向一個惟一的 Meta Class
- 每個 Meta Class 的 isa 指針都指向最上層的 Meta Class,即 NSObject 的 MetaClass,而最上層的 MetaClass 的 isa 指針又指向本身
1.類對象
類對象是由程序員定義並在運行時由編譯器建立的,它沒有本身的實例變量,這裏須要注意的是類的成員變量和實例方法列表是屬於實例對象的,但其存儲於類對象當中的。咱們在objc.h下看看Class的定義:程序員
/// An opaque type that represents an Objective-C class. typedef struct objc_class *Class;
可見,Class是指向C的結構體objc_class的指針,咱們再看一下objc_class的定義(runtime.h):github
struct objc_class { Class _Nonnull isa; // 指向所屬類的指針(_Nonnull) Class _Nullable super_class; // 父類 const char * _Nonnull name; // 類名(_Nonnull) long version; // 類的版本信息(默認爲0) long info; // 類信息(供運行期使用的一些位標識) long instance_size; // 該類的實例變量大小 struct objc_ivar_list * _Nullable ivars; // 該類的成員變量鏈表 struct objc_method_list * _Nullable * _Nullable methodLists; // 方法定義的鏈表 struct objc_cache * _Nonnull cache; // 方法緩存 struct objc_protocol_list * _Nullable protocols; // 協議鏈表 } ;
- isa指針是和Class同類型的objc_class結構指針,類對象的指針指向其所屬的類,即元類。元類中存儲着類對象的類方法,當訪問某個類的類方法時會經過該isa指針從元類中尋找方法對應的函數指針。
- super_class爲該類所繼承的父類對象,若是該類已是最頂層的根類(如NSObject或NSProxy), 則 super_class爲NULL。
- ivars是一個指向objc_ivar_list類型的指針,用來存儲每個實例變量的地址。
- info爲運行期使用的一些位標識,好比:CLS_CLASS (0x1L)表示該類爲普通類, CLS_META (0x2L)則表示該類爲元類。
- methodLists用來存放方法列表,根據info中的標識信息,當該類爲普通類時,存儲的方法爲實例方法;若是是元類則存儲的類方法。
- cache用於緩存最近使用的方法。系統在調用方法時會先去cache中查找,在沒有查找到時纔會去methodLists中遍歷獲取須要的方法。
2.實例對象
實例對象是咱們對類對象alloc或者new操做時所建立的,在這個過程當中會拷貝實例所屬類的成員變量,但並不拷貝類定義的方法。調用實例方法時,系統會根據實例的isa指針去類的方法列表及父類的方法列表中尋找與消息對應的selector指向的方法。一樣的,咱們也來看下其定義(objc.h):objective-c
/// Represents an instance of a class. struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; };
objc_object這個結構體只有一個isa變量,指向實例對象所屬的類。任何帶有以指針開始並指向類結構的結構均可以被視做objc_object, 對象最重要的特色是能夠給其發送消息. NSObject類的alloc和allocWithZone:方法使用函數class_createInstance來建立objc_object數據結構。緩存
/// A pointer to an instance of a class. typedef struct objc_object *id;
id是一個objc_object結構類型的指針。該類型的對象能夠轉換爲任何一種對象,相似於C語言中void *指針類型的做用(objc.h)。ruby
3.元類對象(MetaClass)
顧名思義,元類對象便是描述類對象的類,每一個類都有本身的元類,也就是結構體objc_class中isa指向的類。Objective-C的類方法是使用元類的根本緣由,由於其中存儲着對應的類對象調用的方法即類方法。數據結構
因而可知,在給實例對象或類對象發送消息時,尋找方法列表的規則爲:函數
*當發送消息給實例對象時,消息是在尋找這個對象的類的方法列表(實例方法)ui
*當發送消息給類對象時,消息是在尋找這個類的元類的方法列表(類方法)
全部元類都有一個根元類,好比全部NSObject的子類的元類都會以NSObject的元類做爲他們的類。全部的元類使用根元類做爲他們的類,根元類的元類則就是它本身,也就是說根元類的isa指針指向他本身。
這裏咱們能夠進一步研究一下官方技術文檔runtime.h:
OBJC_EXPORT Class _Nullable object_getClass(id _Nullable obj) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
這裏object_getClass與NSObect.h中的類方法 +(Class)class;和實例方法+ (Class)class;有什麼不一樣呢?請看源碼:
Class object_getClass(id obj) { return _object_getClass(obj); } static inline Class _object_getClass(id obj) { #if SUPPORT_TAGGED_POINTERS if (OBJ_IS_TAGGED_PTR(obj)){ uint8_t slotNumber = ((uint8_t)(uint64_t) obj) & 0x0F; Class isa = _objc_tagged_isa_table[slotNumber]; return isa; } #endif if (obj) return obj->isa; else return Nil; }
_object_getClass函數就是返回對象的isa指針,也就是返回該對象所指向的所屬類。
+ (Class)class { return self; // 返回自身指針 } - (Class)class { return object_getClass(self); // 調用'object_getClass'返回isa指針 }
總結一下實例對象,類對象以及元類對象之間的isa指向和繼承關係的規則爲:
規則一: 實例對象的isa指向該類,類的isa指向元類(metaClass)
規則二: 類的superClass指向其父類,若是該類爲根類則值爲nil
規則三: 元類的isa指向根元類,若是該元類是根元類則指向自身
規則四: 元類的superClass指向父元類,若根元類則指向該根類
四、下面咱們來看一個例子,方便更好的來理解元類
的概念
同時咱們首先理解幾個知識點
object_getClass(實例對象) == [實例對象 class] [類對象 class] == 類對象 object_getClass(類對象) == 類對象的isa == 元類 object_getClass(類對象) != [類對象 class]
添加方法,實際上是在 類對象/實例對象
中的isa指針的類中添加
首先定義一個 Obj的類
---------------------------------------------------- @interface Obj : NSObject //實例方法 - (void)print1; //類對象方法 + (void)classPrint; @end @implementation Obj //這裏不去實現它,後面咱們會經過runtime的知識添加方法 @end
下面咱們切換當控制器中 ----------------------------------------------------
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. Obj * obj = [Obj new]; Class class11 = object_getClass(obj); Class class12 = [obj class]; //這個是獲取當前的類的對象,實例是就是isa指向的類對象,可是類對象指向確是本身自己 NSLog(@"class1 :%p,class2 = %p",class11,class12); //class1 :0x1077b8f00,class2 = 0x1077b8f00 Class class21 = object_getClass(class11);//Son的Class的元類 Class class22 = [class12 class];//仍是自己Son的Class Class classMeta= objc_getMetaClass(object_getClassName(obj));//Son的Class元類 NSLog(@"class1 :%p,class2 = %p,classMeta = %p",class21,class22,classMeta); //class1 :0x1077b8ed8,class2 = 0x1077b8f00,classMeta = 0x1077b8ed8 //在給對象或者類添加方法的時候,實際上是給isa 指向的類添加方法,就是說 一個普通的對象是給它的class添加方法,而 一個普通的類對象,須要添加方法實際上是給它isa指向的 元類添加方法 //給obj實例對象 添加 方法 class_addMethod(class11, @selector(print1), (IMP)IMPFunc, NULL); [obj print1]; //給obj的類對象添加方法 class_addMethod(class21, @selector(classPrint), (IMP)IMPMetaClassFunc, NULL); [Obj classPrint]; } void IMPFunc(id self ,SEL cmd) { NSLog(@"print1"); } void IMPMetaClassFunc(id self ,SEL cmd) { NSLog(@"IMPMetaClassFunc"); }
參考文章
1.[格物致知iOS類與對象] https://mp.weixin.qq.com/s/iBELEyUfnShnLhS5xJh4mQ
2.[清晰理解Objective-C元類]http://blog.csdn.net/beclosedtomyheart/article/details/50164353
3.[Objective-C Runtime 運行時之一:類與對象]http://southpeak.github.io/2014/10/25/objective-c-runtime-1/