類
和元類
的建立時機前面簡單提到類
和元類
的建立時機是在編譯器,今天咱們經過一下兩種方法來驗證一下:c++
類
和元類
的指針首先看下面代碼: 數組
在main函數
以前打印斷點,bash
經過p/x
打印類
指針,若是能得到指針
, 說明已經在內存中申請了內存空間app
而後x/4gx
打印類
的內存結構,獲得類 的isa
,而後isa & 掩碼 ISA_MASK
得到元類
的isa
,若是這個過程當中能正常打印出相應的指針,則能簡單驗證類
和元類
的建立是在編譯期建立的,打印結果以下:函數
command + B
生成可執行文件,而後使用 MachoView
打開程序二進制可執行文件查看由此,能夠驗證類
和元類
是在編譯期建立的,在運行項目alloc
以前已經被建立出來了優化
int a = 10; //
int b = 10; //
LGNSLog(@"%d -- %p",a,&a);
LGNSLog(@"%d -- %p",b,&b);
// KC打印: 10 -- 0x7ffeefbff45c
// KC打印: 10 -- 0x7ffeefbff458
複製代碼
// 對象 - 指針拷貝
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson alloc];
LGNSLog(@"%@ -- %p",p1,&p1);
LGNSLog(@"%@ -- %p",p2,&p2);
// KC打印: <LGPerson: 0x100753be0> -- 0x7ffeefbff450
// KC打印: <LGPerson: 0x10074e740> -- 0x7ffeefbff448
複製代碼
// 數組指針
int c[4] = {1,2,3,4};
int *d = c;
NSLog(@"%p - %p - %p",&c,&c[0],&c[1]);
NSLog(@"%p - %p - %p",d,d+1,d+2);
for (int i = 0; i<4; i++) {
// int value = c[i];
int value = *(d+i);
LGNSLog(@"%d",value);
}
NSLog(@"指針 - 內存偏移");
// 0x7ffeefbff470 - 0x7ffeefbff470 - 0x7ffeefbff474
// 0x7ffeefbff470 - 0x7ffeefbff474 - 0x7ffeefbff478
// KC打印: 1
// KC打印: 2
// KC打印: 3
// KC打印: 4
複製代碼
首地址
是數組
的第一個元素
的地址,&c[0]
和 &c[1]
,相差一個元素的大小,指針d + 1
,至關於偏移一個所佔位數的元素的大小ui
經過clang
查看看下面代碼在c++
文件中的編譯:this
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
複製代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// id, SEL
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
Class pClass = object_getClass(person);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5s_4100t0cd5rn_d7gx0n5wqh8w0000gn_T_main_60f7a3_mi_9,person,pClass);
}
return 0;
}
複製代碼
咱們探究的類
的結構,就是Class
,在cpp
文件中不難發現類
結構是:atom
typedef struct objc_class *Class;
複製代碼
能夠看出,類
是 objc_class
類型的 結構體。spa
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
複製代碼
咱們知道萬物皆對象,objc_class
繼承自objc_object
,那麼咱們經過下圖方法查看objc_class
的源碼:
struct objc_class : objc_object {
// Class ISA; // 8
Class superclass; // 8
cache_t cache; // 16 不是8 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
··· // 方法和函數
}
複製代碼
源碼中能夠看到,有個隱藏的Class isa
(爲何有個隱藏的Class isa
?), 隱藏的屬性必然是來自於繼承
,繼承
自objc_object
,看objc_object
源碼:
object
源碼:
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
複製代碼
那麼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
}
複製代碼
問: 爲何isa
是Class
類型?
答:萬物皆對象,Clss
自己繼承自object
,用來接收isa
能夠的,早期調用isa
就是爲了返回類
, 後期優化了 nonpointer isa
問:objc_class
和NSObject
的關係? objc_object
和NSObject
的關係?
NSObject
是一種objc_class
的類型,NSObject
也是一個類class
,底層也是objc_class
; OC
底層封裝的C
,objc_object
是NSObject
底層編譯的寫法。 objc_object
和objc_class
是底層的實現,對應當前NSObject(Class)
和NSObject
。
一般咱們會在類
中定義屬性
、成員變量
和方法
,
@interface LGPerson : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
複製代碼
那麼在類
中是如何存儲這些定義的屬性 成員變量 方法
的呢? 接下來咱們來研究一下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
複製代碼
經過x/4gx pClass
打印類
結構,經過前面的查看源碼得知以下圖:
objc_class
中 Class ISA
和Class superclass
分別佔8字節
, cache_t cache
佔16字節
struct cache_t {
struct bucket_t *_buckets; // 8
mask_t _mask; // 4 uint32_t mask_t
mask_t _occupied; // 4
public: // 下面是函數,函數不佔內存
struct bucket_t *buckets();
// 方法
···
};
複製代碼
由於objc_class
中cache_t cache
是結構體
,而不是結構體指針佔
(結構體指針佔8字節
), 因此cache_t cache
佔內存8 + 4 + 4 = 16字節
。
猜想:屬性 成員變量
存儲在class_data_bits_t bits
中,經過指針偏移(偏移原理類比爲數組),偏移32字節
獲取class_data_bits_t bits
。
探索以下:
對pClass
首地址0x100001278 + 32
獲得 0x100001298(16進制)
即bits
,經過bits.data()
獲得class_rw_t *data()
,打印以下:
而class_rw_t
的結構以下:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
複製代碼
打印*data()
:
屬性
應該存儲在
properties
中,打印
properties
,而後並打印其中
list
:
同理打印
methods
,一系列操做後以下:
由此咱們探究出了屬性 方法
的存儲位置,那麼成員變量
存儲在什麼地方呢?
經過查看struct class_rw_t
中的const class_ro_t *ro
,
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
複製代碼
裏面分別有method_list_t * baseMethodList
property_list_t *baseProperties
const ivar_list_t * ivars
,咱們猜想類
的方法
屬性
成員變量
分別存儲在對應的變量中,打印ro
結果以下:
由此能夠看出LGPerson
僅有的一個成員變量 nickName
存儲在bit.data()
中的ro
中baseProperties
中,
那麼爲何bit.data()
中property_array_t properties
也等打印出成員變量
呢?暫時先拋出個問題。
接下來咱們用一樣的方法分別打印ivars
baseMethodList
,如圖:
baseMethodList
打印:
打印出count = 2
,分別打印ivars
中成員變量
,分別爲hobby
_nickName
,再次驗證了@property
生成的屬性
,在系統底層會自動生成_屬性
的成員變量
,而且會自動生成setter
getter
。
問題:從baseMethodList
中並未打印出類方法 sayHappy
,那麼類方法
存儲在什麼地方呢?
猜想: 實例方法
存在 類
中,那麼其實 類
也是元類
建立出來的類對象
,類
的類方法
應該存在元類
中。
經過下面代碼,分別在類
和元類
中打印對象方法
和類方法
:
void testInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
打印結果
2019-12-29 12:28:17.714554+0800 LGTest[799:13098] 0x100002198-0x0-0x0-0x100002130
2019-12-29 12:28:17.715541+0800 LGTest[799:13098] testInstanceMethod_classToMetaclass
複製代碼
由打印結果看出,對象方法
存在於類
中,不存在於元類
中,類方法
存在於元類
中,不存在於類
中。
類
結構的分析,得出:成員變量
存在ivars
中,屬性
存儲在baseProperties
中,對象方法
存儲在類
裏面,類方法
存儲在元類
裏。