runtime - iOS類對象、實例對象、元類對象


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

我們先看一張圖:

 

 
iOS類、實例、元類關系圖.jpg
  • 每個 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的類方法是使用元類的根本原因,因為其中存儲着對應的類對象調用的方法即類方法。

 

 
類存儲示意圖.jpg

 

由此可見,在給實例對象或類對象發送消息時,尋找方法列表的規則為:

*當發送消息給實例對象時,消息是在尋找這個對象的類的方法列表(實例方法)

*當發送消息給類對象時,消息是在尋找這個類的元類的方法列表(類方法)

所有元類都有一個根元類,比如所有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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM