對於一門編程語言而言,當初學者掌握了其基本語法和標準庫的使用之後,若是可以繼續瞭解該語言的核心思想與底層實現,則會漸入佳境,窺探語言實現的本質技術,可以爲之後的性能優化以及規避技術陷阱等複雜工做提供思路。 瞭解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,那麼類具體有以下表現數組
對象是一個根據類實例化出來的數據結構。具備實例方法,實例變量,對象沒有繼承概念,只有持有其餘對象或被其餘對象持有,具備如下特色。緩存
既然類和對象只不過是特色不一樣的自定義數據類型,那麼類和對象必然要使用結構體實現,Objetcive-C也是這樣設計的,咱們看一下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*
複製代碼
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指針來尋找到其類的信息所在的結構體。 該結構體中有幾個比較重要的變量:數據結構
類的實例->對象也是經過一個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根類數據結構
至此,能夠很是清晰得出如下結論: 以下圖所示:
即:
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_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;
}
複製代碼
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快速找到其對應實現。
//成員變量操做函數
// 獲取類中指定名稱實例成員變量的信息
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 );
複製代碼
// 添加方法
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:
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這門語言可以給予程序員很是大的自由度去享受編程的樂趣。