前言
為什么要寫NSObject呢?嗯嗯,主要因為手賤。本來,近段時間公司比較閑,然后,鄙人想進階一下iOS開發,runtime之前看過一些簡書,blog等等,但是實際上用的比較少,也不是很清晰,所以就去看runtime的相關資料,然后發現很多不懂的知識,就不斷點連接,看看跳跳,然后就來到了NSObject對象模型,索性就先把它弄明白。推薦一篇深入淺出的文章《NSObject對象模型解析(上)》。
1.先看一下結構
大家可以先看一下NSObject.h頭文件
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
- (Class)class;
上面可以看見,NSObject含有isa成員變量,是一個Class類型,繼續點進去,到objc.h。
typedef struct objc_class *Class;
Class是一個結構體指針,現在我們知道了isa是一個objc_class結構體指針。繼續點objc_class進去,到runtime.h。
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。現在,我們來分析一下:
isa //指向本類meta-class的結構體指針,meta-class存在的意義在于,調用類方法和調用實例方法是同一套消息傳遞機制。
super_class //看名字就知道指向本類的結構體指針
name //類名
version //類版本信息
info //類相關的一些信息
instance_size //類所有實例變量的大小
ivars //類的成員變量列表
methodLists //函數列表
cache //緩存使用過的方法列表,提高訪問速度
protocols //協議列表
類的結構大概就是這樣的東西了,來實際編譯一下看源代碼
新一個類:ClassA
ClassA.h
@protocol ClassAProtocol <NSObject>
- (void)protocolMethod;
@end
@interface ClassA : NSObject<ClassAProtocol>
@property (nonatomic, strong) NSString *publicStr1;
@property (nonatomic, copy) NSString *publicStr2;
+ (void)classMethod;
- (void)publicMethod;
@end
ClassA.m
@interface ClassA ()
@property (nonatomic, strong) NSString *privateStr1;
@property (nonatomic, copy) NSString *privateStr2;
@end
@implementation ClassA {
NSString *privateStr3;
}
#pragma mark - Class method
+ (void)classMethod {}
#pragma mark - Public
- (void)publicMethod {}
#pragma mark - Private
- (void)privateMethod {}
#pragma mark - ClassAProtocol
- (void)protocolMethod {}
@end
打開終端,進入ClassA.m文件所在目錄,執行下面命令
clang -rewrite-objc ClassA.m
打開生成的ClassA.cpp文件,定位到最底端,我們來一一分析。
找到ClassA.h聲明頭文件
#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$_publicStr1;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_publicStr2;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr1;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr2;
struct ClassA_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *privateStr3;
NSString *_publicStr1;
NSString *_publicStr2;
NSString *_privateStr1;
NSString *_privateStr2;
};
上面可以看到我們定義的屬性和成員變量,我們再往下看:
static void _C_ClassA_classMethod(Class self, SEL _cmd) {}
static void _I_ClassA_publicMethod(ClassA * self, SEL _cmd) {}
static void _I_ClassA_privateMethod(ClassA * self, SEL _cmd) {}
static void _I_ClassA_protocolMethod(ClassA * self, SEL _cmd) {}
找到類方法,公有實例方法和私有實例方法,繼續往下看。
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
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;
};
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
上面可以看到method,protocol,ivar以及class結構體定義,重點放在_class_ro_t這個結構體,是不是和runtime里面定義的objc_class結構體有點像。再看一下_class_t這個結構體,是不是除了包含了isa指針和superclass指針還包含_class_ro_t這個結構體,答案呼之欲出了,_class_t結構體就是objc_class編譯成c的結構。下面繼續看一下成員變量和屬性變成什么樣子。
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[5];
} _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
5,
{{(unsigned long int *)&OBJC_IVAR_$_ClassA$privateStr3, "privateStr3", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr1, "_publicStr1", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr2, "_publicStr2", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr1, "_privateStr1", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr2, "_privateStr2", "@\"NSString\"", 3, 8}}
};
如上所示,成員變量都變成了類似OBJC_IVAR_$_ClassA$privateStr3這樣的結構,很容易就能看出來。看一下方法列表:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[11];
} _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
11,
{{(struct objc_selector *)"publicMethod", "v16@0:8", (void *)_I_ClassA_publicMethod},
{(struct objc_selector *)"privateMethod", "v16@0:8", (void *)_I_ClassA_privateMethod},
{(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_ClassA_protocolMethod},
{(struct objc_selector *)"publicStr1", "@16@0:8", (void *)_I_ClassA_publicStr1},
{(struct objc_selector *)"setPublicStr1:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr1_},
{(struct objc_selector *)"publicStr2", "@16@0:8", (void *)_I_ClassA_publicStr2},
{(struct objc_selector *)"setPublicStr2:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr2_},
{(struct objc_selector *)"privateStr1", "@16@0:8", (void *)_I_ClassA_privateStr1},
{(struct objc_selector *)"setPrivateStr1:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr1_},
{(struct objc_selector *)"privateStr2", "@16@0:8", (void *)_I_ClassA_privateStr2},
{(struct objc_selector *)"setPrivateStr2:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr2_}}
};
實例方法都變成了objc_selector的結構體指針,還包含系統默認自定義的get和set的方法,但是仔細一看,貌似方法列表少了類方法。為什么呢?大家看一下這個結構體的命名_OBJC$_INSTANCE_METHODS_ClassA,是不是想到了什么?是的,這個只是實例方法,類方法繼續往下看:
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 *)"classMethod", "v16@0:8", (void *)_C_ClassA_classMethod}}
};
_OBJC$_CLASS_METHODS_ClassA,這個命名夠明顯了吧,類方法列表。等等,我們是不是忘記了什么?有人會說,協議呢?協議方法呢?別急,下面給你呈現出來。
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
};
struct _protocol_t _OBJC_PROTOCOL_ClassAProtocol __attribute__ ((used)) = {
0,
"ClassAProtocol",
(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_ClassAProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol,
0,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_ClassAProtocol
};
是不是看見自己定義的協議和協議方法了。嘿嘿嘿,客官可真是慧眼呀??。再往下看。
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"publicStr1","T@\"NSString\",&,N,V_publicStr1"},
{"publicStr2","T@\"NSString\",C,N,V_publicStr2"}}
};
顯而易見,上面就是屬性列表。我們之前ClassA定義的屬性,成員變量,方法,協議都能找到相應的實現。
2.解析一下類(Class)與元類(meta-class)的關系
先上一張大家很熟悉的圖:
先說一下,一個類的isa指針,meta-class以及superclass怎么獲取,代碼呈上:
Class object_getClass(id obj) //獲取isa指針
objc_getMetaClass(const char *name) //獲取meta-class
objc_getClass(const char *name) //獲取本類
class_getSuperclass(Class cls) //獲取父類
下面解析一下這張圖怎么得到的:
新建兩個類:ClassA繼承NSObject,ClassB繼承ClassA
@interface ClassA : NSObject
@end
@interface ClassB : ClassA
@end
同樣,clang一下,編譯成.cpp文件,我們再來逐步分析。
先看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;
}
從上面的代碼我們可以看出來:
ClassA.isa->ClassA_Meta_Class
ClassA.superclass->NSObject_Class
ClassA_Meta_Class.isa->NSObject_Meta_Class
ClassA_Meta_Class.superclass->NSObject_Meta_Class
我們再看ClassB:
static void OBJC_CLASS_SETUP_$_ClassB(void ) {
OBJC_METACLASS_$_ClassB.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_ClassB.superclass = &OBJC_METACLASS_$_ClassA;
OBJC_METACLASS_$_ClassB.cache = &_objc_empty_cache;
OBJC_CLASS_$_ClassB.isa = &OBJC_METACLASS_$_ClassB;
OBJC_CLASS_$_ClassB.superclass = &OBJC_CLASS_$_ClassA;
OBJC_CLASS_$_ClassB.cache = &_objc_empty_cache;
}
同樣,我們可以得到:
ClassB.isa->ClassB_Meta_Class
ClassB.superclass->ClassA
ClassB_Meta_Class.isa->NSObject_Meta_Class
ClassB_Meta_Class.superclass->ClassA_Meta_Class
結合ClassA和ClassB我們可以得出下圖:
是不是覺得和上面的圖還是有點不一樣,不著急,現在我們通過代碼來補全這張圖,順便驗證一下剛才畫的邏輯圖的正確性。GoGoGo!
ClassB *classB = [ClassB new];
Class myClass = [classB class];
while (myClass != nil) {
Class superClass = class_getSuperclass(myClass);
Class metaClass = object_getClass(myClass);
Class superMetaClass = class_getSuperclass(metaClass);
Class isaMetaClass = object_getClass(metaClass);
const char *myClassName = class_getName(myClass);
NSString *className = [NSString stringWithUTF8String: myClassName];
NSLog(@"%@: %p\n%@_superClass: %p\n%@_meta_class: %p\n%@_meta_superclass: %p\n%@_isa_meta_class: %p\n", className, myClass, className, superClass, className, metaClass, className, superMetaClass, className, isaMetaClass);
myClass = superClass;
}
//輸出
2017-03-02 22:05:49.520227 ObjCRuntimeDemo[9490:695334] ClassB: 0x100003b38
ClassB_superClass: 0x100003bd8
ClassB_meta_class: 0x100003b10
ClassB_meta_superclass: 0x100003bb0
ClassB_isa_meta_class: 0x7fffaec320f0
2017-03-02 22:05:49.520509 ObjCRuntimeDemo[9490:695334] ClassA: 0x100003bd8
ClassA_superClass: 0x7fffaec32140
ClassA_meta_class: 0x100003bb0
ClassA_meta_superclass: 0x7fffaec320f0
ClassA_isa_meta_class: 0x7fffaec320f0
2017-03-02 22:05:49.520570 ObjCRuntimeDemo[9490:695334] NSObject: 0x7fffaec32140
NSObject_superClass: 0x0
NSObject_meta_class: 0x7fffaec320f0
NSObject_meta_superclass: 0x7fffaec32140
NSObject_isa_meta_class: 0x7fffaec320f0
Program ended with exit code: 0
//每個類需要打印5個地址,分別是本類的地址,本類繼承的父類地址,本類元類地址,本類元類父類地址,本類元類isa指針指向的地址
由上述代碼以及輸出,我們驗證了第一張圖的正確性,并且進一步完善了第一張圖,如下:
看上去是不是和最開始的邏輯圖很像,再加上實例對象的isa指針的指向,替換一下子類,父類和根類的概念,完全就一樣了。所以說,類和元類之前的關系,就如上圖,每個類都有特定的元類,類的isa指針指向元類,元類擁有著類一樣的結構,并且每個元類的isa指針都是指向根元類,而元類的出現就是為了讓類方法的調用和實例方法保持一致。下面看類和元類編譯的c代碼:
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_ClassA,
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
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,
};
//meta_class和class類型都是_class_t結構體
3.總結
上面說了一大堆,廢話也不少,第一次這么賣力寫文章,說起來都有點感動??。其實,主要就是NSObject對象模型的解析,以及類和元類的關系,大家有興趣的自己去編譯一下,肯定會有收獲的。