Objective-C 面向對象(一)——對象的本質

對於一門編程語言而言,當初學者掌握了其基本語法和標準庫的使用之後,若是可以繼續瞭解該語言的核心思想與底層實現,則會漸入佳境,窺探語言實現的本質技術,可以爲之後的性能優化以及規避技術陷阱等複雜工做提供思路。 瞭解Objective-C語言的面向對象的本質,有益於程序員更加深入理解該語言是如何實踐OOP思想,並使得在構建面向對象程序的時候更加遊刃有餘。程序員

背景知識

Objective-C是C語言的超集,也就是說,C語言的語法所有被Objective-C兼容,而面向對象的特性則是創建在C語言的基礎之上,當熟悉過C語言的指針、內存管理、自定義數據-結構體等一系列知識之後,對於Objective-C的面向對象實現的理解,就容易多了,由於本質上,Objective-C的面向對象,就是使用這些東西構建出來的。 咱們須要瞭解的是,對於C語言來講,除了語言自己定義的數據類型,程序員想要自定義數據類型以供編程使用,結構體是必然選擇,基於這樣的事實,那麼理應可以猜到,Objective-C中的一切面向對象概念,諸如類、對象等,都是基於C語言的結構體之上構建的,而如何進行對象方法的調用、類方法調用等等,則經過Objective-C從smalltalk借鑑過來的消息調用思想而實現的Runtime思想,後者是消息調用思想的鼻祖。shell

什麼是類和對象

C語言是沒有面向對象概念的,只有基本數據類型、指針、結構體等等。那麼如何經過這些概念構建面向對象的概念,要明白這個的前提是大致總結一下對象和類有什麼特色。編程

類是描述一個對象規格的模板,即它說明了一個對象有什麼樣的屬性,有什麼樣的方法。對象的構建,經過指定類,而且初始化,從而獲得類的實例-對象,那麼也就是說類是一種描述實例對象的數據結構。 在Objective-C中,標準庫爲Foundation,事實上幾乎全部的類都繼承與NSObject,那麼類具體有以下表現數組

  1. 類具備方法和類方法的聲明,描述對象實例有什麼方法和類有什麼方法
  2. 類具備屬性的聲明,描述對象實例有什麼樣的屬性
  3. 類能夠被集成或集成其餘類,從而給他人提供或從父類獲取對象描述的規格信息

對象

對象是一個根據類實例化出來的數據結構。具備實例方法,實例變量,對象沒有繼承概念,只有持有其餘對象或被其餘對象持有,具備如下特色。緩存

  1. 對象具備實例方法
  2. 對象具備實例屬性
  3. 對象能夠被其餘對象持有或持有其餘對象

類和對象的實現

既然類和對象只不過是特色不一樣的自定義數據類型,那麼類和對象必然要使用結構體實現,Objetcive-C也是這樣設計的,咱們看一下NSObject的定義:性能優化

類的定義(NSObject)

*@interface* NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
+ (void)load;
+ (void)initialize;
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
    NS_DESIGNATED_INITIALIZER
#endif
    ;
+ (instancetype) new  OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)allocWithZone:(struct _NSZone )zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");

*@end*
複製代碼

如何經過C的數據結構實現Objective-C的對象和類

NSObject的定義中,有類方法定義、屬性定義、實例方法定義,如何使用C語言的結構體來表達和存儲這樣的自定義數據結構呢?NSObject是一個Class也就是一個類,在描述中有一個Class isa的變量,按圖索驥查找到Class的數據結構:bash

typedef struct objc_class *Class; //class是一個objc_class結構體的指針
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
    struct objc_cache *cache OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE; //objc_class是一個結構體,描述了一個類的信息
複製代碼

從上面定義能夠看出來,一個類包含哪些信息,是經過objc_class 結構體來表示的,NSObject的定義中,有一個Class isa屬性,而Class是一個指向objc_class結構體的指針,也就是說,NSObject經過isa指針來尋找到其類的信息所在的結構體。 該結構體中有幾個比較重要的變量:數據結構

  • Class isa OBJC_ISA_AVAILABILITY; 又是一個指向objc_class結構體的指針,指向另一個類信息的結構體,那麼一個類指向一個說明其規格的類結構體,其意義是來描述類的信息,通常稱描述類的結構的數據類型稱之爲元類,即meta-class,覺得着使用元類來描述類的規格,那麼從對象與類的關係類比中,能夠將類看做是元類的實例,也就說,元類是類對象的類。
  • super_class 是該類父類的信息,使用super_class指針,找到父類信息的結構體。NSObject的實例對象的superclass爲null
  • long info OBJC2_UNAVAILABLE; 類信息,供運行期使用的一些位標識
  • long instance_size OBJC2_UNAVAILABLE; /該類的實例變量大小
  • struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 類的成員變量鏈表
  • struct objc_method_list methodLists OBJC2_UNAVAILABLE; 方法定義的鏈表
  • struct objc_cache *cache OBJC2_UNAVAILABLE; 方法緩存,對象接到一個消息會根據isa指針查找消息對象,這時會在methodLists中遍歷,若是cache了,經常使用的方法調用時就可以提升調用的效率。
  • struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 協議鏈表

類的實例->對象也是經過一個objc_class結構體描述其結構。以下:編程語言

struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};

typedef struct objc_object *id;
複製代碼

id 類型,即對象,其爲一個指向objc_object結構體的指針,意味着任意一個Objective-C的對象,其本質是一個指向objc_object的結構體指針,而objc_object結構體中,有一個isa指針,指向objc_class結構體,來描述其屬於哪一個類,也就是上面的類信息結構體。函數

查看根類、元類與子類的具體實現

當定義一個類的時候,以下:

//main.m
@interface ClassA : NSObject
@property(nonatomic,copy)NSString * name;
-(void)sayHello;
+(void)SayHello;
@end
@implementation ClassA
-(void)sayHello{
    NSLog(@"object say hello");
}
+(void)SayHello{
    NSLog(@"class say hello");
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
        NSObject * obj1 = [[NSObject alloc]init];
        ClassA * obj2 = [[ClassA alloc]init];
        [obj2 sayHello];
        [ClassA SayHello];
        NSLog(@"%@", NSStringFromClass([obj1 superclass]));
    }
    return 0;
}
複製代碼

從上面的定義中,按照以前的說明,將該文件轉換爲C++代碼,將在C++代碼中獲得確切的信息。

# 獲得main.cpp文件
clang -rewrite-objc main.m 
複製代碼

對於NSObject這個類,能夠獲得:

#ifndef _REWRITER_typedef_NSObject
#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif
複製代碼

NSObject是一個objc_object結構體。 ClassA的結構以下:

typedef struct objc_object ClassA;
複製代碼

ClassA是一個指向objc_object結構體,其相關的其餘部分爲:

#ifndef _REWRITER_typedef_ClassA
#define _REWRITER_typedef_ClassA
typedef struct objc_object ClassA;
typedef struct {} _objc_exc_ClassA;
#endif

extern "C" unsigned long OBJC_IVAR_$_ClassA$_name;
struct ClassA_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name;
};

// @property(nonatomic,copy)NSString * name;
// -(void)sayHello;
// +(void)SayHello;
/* @end */

// @implementation ClassA

static void _I_ClassA_sayHello(ClassA * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_0);
}

static void _C_ClassA_SayHello(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_1);
}

static NSString * _I_ClassA_name(ClassA * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ClassA$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_ClassA_setName_(ClassA * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ClassA, _name), (id)name, 0, 1); }
// @end
複製代碼

這個結構中,清楚地描述出了ClassA類中的實例變量、類方法、實例方法的結構和實現。 ClassA是一個objc_object結構體,其類方法和靜態方法在聲明之後,被使用在以下:

extern "C" unsigned long int OBJC_IVAR_$_ClassA$_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ClassA, _name);

static struct /*_ivar_list_t*/ {
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count;
	struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_ivar_t),
	1,
	{{(unsigned long int *)&OBJC_IVAR_$_ClassA$_name, "_name", "@\"NSString\"", 3, 8}}
};

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	3,
	{{(struct objc_selector *)"sayHello", "v16@0:8", (void *)_I_ClassA_sayHello},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_ClassA_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_ClassA_setName_}}
};

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"SayHello", "v16@0:8", (void *)_C_ClassA_SayHello}}
};

static struct /*_prop_list_t*/ {
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_prop_t),
	1,
	{{"name","T@\"NSString\",C,N,V_name"}}
};
複製代碼

OBJC$_INSTANCE_VARIABLES_ClassA

OBJC$_INSTANCE_METHODS_ClassA

OBJC$_CLASS_METHODS_ClassA

OBJC$_PROP_LIST_ClassA

這四個結構體成員變量爲類的實例屬性、實例方法列表與類方法列表等定義的結構體,這些結構體的被用到在:

static struct _class_ro_t _OBJC_CLASS_RO_$_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	0, __OFFSETOFIVAR__(struct ClassA, _name), sizeof(struct ClassA_IMPL), 
	(unsigned int)0, 
	0, 
	"ClassA",
	(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_ClassA,
	0, 
	(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_ClassA,
	0, 
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_ClassA,
};
複製代碼

OBJC_CLASS_RO$_ClassA

結構體變量將類方法、屬性等結構體進行包裝,其數據類型_class_ro_t以下:

struct _class_ro_t {
	unsigned int flags;
	unsigned int instanceStart;
	unsigned int instanceSize;
	unsigned int reserved;
	const unsigned char *ivarLayout;
	const char *name;
	const struct _method_list_t *baseMethods;
	const struct _objc_protocol_list *baseProtocols;
	const struct _ivar_list_t *ivars;
	const unsigned char *weakIvarLayout;
	const struct _prop_list_t *properties;
};
複製代碼

該結構體其實就是objc_class結構體的變形,表明根類的結構體類型。

OBJC_CLASS_RO$_ClassA

又被另一個結構體進行包裝:

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
	0, // &OBJC_METACLASS_$_ClassA,
	0, // &OBJC_CLASS_$_NSObject,
	0, // (void *)&_objc_empty_cache,
	0, // unused, was (void *)&_objc_empty_vtable,
	&_OBJC_CLASS_RO_$_ClassA,
};
複製代碼

OBJC_CLASS_$_ClassA

則就是ClassA這個類的結構體,其結構體是_class_t。

struct _class_t {
	struct _class_t *isa;
	struct _class_t *superclass;
	void *cache;
	void *vtable;
	struct _class_ro_t *ro;
};
複製代碼

OBJC_CLASS_$_ClassA

OBJC_CLASS_RO$_ClassA

兩個結構體被用在:

static void OBJC_CLASS_SETUP_$_ClassA(void ) {
	OBJC_METACLASS_$_ClassA.isa = &OBJC_METACLASS_$_NSObject;
	OBJC_METACLASS_$_ClassA.superclass = &OBJC_METACLASS_$_NSObject;
	OBJC_METACLASS_$_ClassA.cache = &_objc_empty_cache;
	OBJC_CLASS_$_ClassA.isa = &OBJC_METACLASS_$_ClassA;
	OBJC_CLASS_$_ClassA.superclass = &OBJC_CLASS_$_NSObject;
	OBJC_CLASS_$_ClassA.cache = &_objc_empty_cache;
}
複製代碼

這是一個類初始化函數,從函數中獲得明確的信息是:

OBJC_METACLASS_$_ClassA

是一個_class_t(即objc_class)結構體,其isa指針指向OBJC_METACLASS_$_NSObject結構體,其類型爲_class_ro_t(objc_class)的結構體

OBJC_METACLASS_$_ClassA.superclass

指明其父類是OBJC_METACLASS_$_NSObject結構體指針

OBJC_CLASS_$_ClassA

其isa指針指向ClassA元類結構

OBJC_METACLASS_$_ClassA

其次

OBJC_CLASS_$_ClassA.superclass

指明其父類是OBJC_CLASS_$_NSObject根類數據結構

實例對象、類、父類、根類、元類的簡單關係

  • 實例對象,在Objective-C中即爲一個objc_object的結構體指針
  • 類,也是一個objc_object的結構體指針
  • 父類,子類經過isa指向的objc_object的結構指針
  • 根類,NSObject,全部子類都直接或間接的經過isa指針指向該類實例對象
  • 元類,類的objc_object結構體中的isa指針指向的objc_class結構體

至此,能夠很是清晰得出如下結論: 以下圖所示:

關係圖

  1. ClassA的實例對象,是一個objc_object結構體指針,其isa指針指向ClassA類的objc_object結構體
  2. ClassA,是一個_class_t(即objc_class)的結構體,其isa指針指向MetaClassA結構體
  3. ClassA的父類,是NSObject,其superclass是一個指向NSObject類對象的isa指針
  4. ClassA的元類,是MetaClassA,OBJC_METACLASS_元類結構體代表,其isa指針指向MetaNSObject結構體指針
  5. 一個類所擁有的方法、屬性,都會存儲在類的元類中,當調用對象的方法的時候,也就是向對象發送消息,runtime會在這個對象所屬的類方法列表中查找消息對應的方法,但向類發送消息的時候,runtime就會在這個類的meta class的方法列表中查找。 更加通用和清晰的關係圖以下:

關係圖

即:

  1. 對象的isa指針指向類對象
  2. 對象的superclass的指針指向父類類對象
  3. 類對象的isa指針指向元類
  4. 類對象的superclass的指針指向父類元類
  5. 元類的isa指針,指向根類(NSObject)元類
  6. 元類的superclass指針指向父類元類,直接繼承根類的類的元類的superclass指向根類元類(NSObject)
  7. 根類(NSObject)的isa,指向根元類
  8. 根類(NSObject)的superclass爲nil
  9. 根元類的superclass指向NSObject類
  10. 根元類的isa指針,指向自身

類與對象的相關信息

獲取類名

const char * class_getName(Class cls);
複製代碼

父類與超類的獲取

//獲取父類
Class class_getSuperclass(Class cls);
//斷定類是否爲一個meta class
BOOL class_isMetaClass(Class cls);
複製代碼

計算類的被分配的大小

size_t class_gerInstanceSize(Class cls);
複製代碼

實例對象和類的屬性列表objc_ivar_list與方法列表objc_method

//objc_ivar_list結構體存儲objc_ivar數組列表
struct objc_ivar_list {
     int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
     int space OBJC2_UNAVAILABLE;
#endif
     /* variable length structure */
     struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;

//objc_method_list結構體存儲着objc_method的數組列表
struct objc_method_list {
     struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
     int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
     int space OBJC2_UNAVAILABLE;
#endif
     /* variable length structure */
     struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
複製代碼

objc_ivar_list 爲成員變量單向鏈表,其結構體最後一個成員變量是一個objc_ivar結構體數組,該數組爲變長,因此objc_ivar_list能夠是一個變長結構體,objc_ivar標示一個成員變量:

struct objc_ivar {
    char * _Nullable ivar_name  OBJC2_UNAVAILABLE;//變量名
    char * _Nullable ivar_type  OBJC2_UNAVAILABLE;//變量類型 
    int ivar_offset   OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space    OBJC2_UNAVAILABLE;
#endif
}   OBJC2_UNAVAILABLE;
複製代碼

objc_method_list爲方法列表

struct objc_method_list {
    struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE;
    int method_count  OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space    OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]  OBJC2_UNAVAILABLE;
}            
複製代碼

objc_method爲方法結構體,以下:

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}         
複製代碼

Objective-C的方法實現(IMP)與方法簽名(SEL)

typedef struct  *SEL;
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif
複製代碼

IMP是一個void * ()的函數指針,實際上就是方法的實現,SEL爲一個char * 字符串。 每個objc_class結構體中都有objc_method_list列表,而objc_method_list列表中有objc_method結構體,該結構體爲一個SEL對應一個IMP實現。 在runtime運行的時候,加載的每個類對應有一個virtual table,用來緩存SEL與IMP的對應關係,從而可以經過SEL快速找到其對應實現。

成員變量(ivars)及其屬性

//成員變量操做函數
// 獲取類中指定名稱實例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );

// 獲取類成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); //這個只可以向在runtime時建立的類添加成員變量

// 獲取整個成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); //必須使用free()來釋放這個數組

//屬性操做函數
// 獲取類中指定名稱實例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );

// 獲取類成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );

// 獲取整個成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
複製代碼

方法列表methodLists的運行時操做

// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); //和成員變量不一樣的是能夠爲類動態添加方法。若是有同名會返回NO,修改的話須要使用method_setImplementation

// 獲取實例方法
Method class_getInstanceMethod ( Class cls, SEL name );

// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );

// 獲取全部方法的數組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

// 替代方法的實現
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

// 返回方法的具體實現
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

// 類實例是否響應指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
複製代碼

描述類的objc_protocol_list與Protocol

objc_protocol_list:

struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};
複製代碼

Protocol的定義以下:

#ifdef __OBJC__
@class Protocol;
#else
typedef struct objc_object Protocol;
#endif
複製代碼

對protocol的操做爲:

// 添加協議
BOOL class_addProtocol ( Class cls, Protocol *protocol );

// 返回類是否實現指定的協議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );

// 返回類實現的協議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
複製代碼

總結

Objective-C基於C語言的結構體定義面向對象的大部分概念,利用結構體指針與函數指針,來實現面向對象的類定義、類繼承、實例化對象、對象成員變量和方法的存儲與定義。 由此,Objective-C這本語言是一種在運行時發揮強大能力的語言,而這又歸功於runtime這一消息分發系統,在運行時可以對類進行掃描、查找、調用、修改等等,這部分知識被稱爲rumtime核心技術,消息調用與動態類型的結合,使得Objective-C這門語言可以給予程序員很是大的自由度去享受編程的樂趣。

相關文章
相關標籤/搜索