理解類與對象的本質對於掌握一門語言是至關重要的,本文將從結構類型的角度探討OC的類對象、實例對象、元類對象(Meta Class)。
我們先看一張圖:

- 每個 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):
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):
/// 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)。
3.元類對象(MetaClass)
顧名思義,元類對象即是描述類對象的類,每個類都有自己的元類,也就是結構體objc_class中isa指向的類。Objective-C的類方法是使用元類的根本原因,因為其中存儲着對應的類對象調用的方法即類方法。

由此可見,在給實例對象或類對象發送消息時,尋找方法列表的規則為:
*當發送消息給實例對象時,消息是在尋找這個對象的類的方法列表(實例方法)
*當發送消息給類對象時,消息是在尋找這個類的元類的方法列表(類方法)
所有元類都有一個根元類,比如所有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指向父元類,若根元類則指向該根類
4、下面我們來看一個例子,方便更好的來理解元類
的概念
同時我們首先理解幾個知識點
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/