1、在開始前先說下怎么將oc代碼轉(zhuǎn)為c++代碼
方法1
1、打開終端cd到目標(biāo)的工程文件
2、終端輸入:clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxx.m,其中xxx.m替換成自己需要轉(zhuǎn)換的文件,然后敲回車
方法2
1、打開終端cd到目標(biāo)的工程文件
2、xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx.m -o xxx.cpp,將xxx改為自己需要轉(zhuǎn)換的文件名才回車就可以了
如果需要鏈接其他框架,使用-framework參數(shù)。比如-framework UIKit
在終端上執(zhí)行了上面兩個(gè)方法中任何一個(gè)后,回到工程文件中就可以看到多了一個(gè)cpp文件,將cpp文件拖拽到工程中就可以在xcode里看到了
注意
在將cpp文件添加到工程中后最好將cpp文件從編譯器中移除,否則在編譯的時(shí)候會報(bào)錯(cuò)。

oc轉(zhuǎn)c\c++詳細(xì)流程可以看這里iOS將oc的.m文件編譯成C++的.cpp文件
2、oc的底層實(shí)現(xiàn)
- oc的底層實(shí)現(xiàn)都是c\c++代碼,oc的面向?qū)ο蠖际腔赾\c++的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的
- oc的對象和類主要是基于c\c++的結(jié)構(gòu)體實(shí)現(xiàn)的
-
編譯器會先將oc代碼轉(zhuǎn)化為c\c++代碼,再將c\c++代碼轉(zhuǎn)化為匯編語言,然后再轉(zhuǎn)化為機(jī)器語言
1-2.png
下面我們創(chuàng)建一個(gè)NSObject對象,然后再轉(zhuǎn)化成c++代碼,看下在c++中是什么樣的結(jié)構(gòu)
NSObject * object = [[NSObject alloc] init];
在oc中NSObject的定義是這樣的
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
在c++中是一個(gè)結(jié)構(gòu)體
struct NSObject_IMPL {
Class isa;
};
由此可以證明上面的結(jié)論oc的對象和類主要是基于c\c++的結(jié)構(gòu)體實(shí)現(xiàn)的
class又是什么呢?clas是一個(gè)指針
typedef struct objc_class *Class;
如果創(chuàng)建一個(gè)Person類繼承自NSObject其底層又是怎樣實(shí)現(xiàn)的呢?
Person * person = [[Person alloc] init];
轉(zhuǎn)化為c++后
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
NSObject_IMPL就是NSObject的底層
struct NSObject_IMPL {
Class isa;
};
那么可以在Person_IMPL中將NSObject_IMPL看成是isa指針,那么就等價(jià)于下面的寫法
struct Person_IMPL {
Class isa;
};
如果Person帶有成員變量呢?
@interface Person : NSObject
{
int _age;
NSString * _name;
}
其底層為
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
NSString *_name;
};
如果再創(chuàng)建一個(gè)Student類繼承自Person并帶有了height成員變量呢?
@interface Student : Person
{
int _height;
}
轉(zhuǎn)化為c++后
struct Student_IMPL {
struct Person_IMPL Person_IVARS;
int _height;
};
- 由上面的Person和Student轉(zhuǎn)化為c++后可以看出,子類中包含了父類的結(jié)構(gòu)體
- 每個(gè)對象都包含一個(gè)isa指針
3、oc對象內(nèi)存詳解
下面先用兩個(gè)方法去打印NSObject對象的內(nèi)存大小
NSObject * object = [[NSObject alloc] init];
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
NSLog(@"%zd",malloc_size((__bridge const void *)object));
這兩個(gè)方法分別打印的是8和16,為什么這兩個(gè)方法打印出來的內(nèi)存大小不一樣呢。
class_getInstanceSize是獲取一個(gè)實(shí)例對象創(chuàng)建至少需要多少內(nèi)存
malloc_size是獲取創(chuàng)建一個(gè)實(shí)例對象,實(shí)際上分配了多少內(nèi)存
為什么會有一個(gè)最少內(nèi)存和一個(gè)分配內(nèi)存呢?因?yàn)閛c中有一個(gè)內(nèi)存對齊規(guī)則。
內(nèi)存對齊:簡單的理解就是最終的內(nèi)存大小為成員中內(nèi)存最大的整數(shù)倍,不足的要對齊。
在64位的環(huán)境下oc中對象存取是以8字節(jié)來計(jì)算的,對象開辟空間的內(nèi)存是以16字節(jié)來對齊的。
想要詳細(xì)了解iOS中內(nèi)存對齊的可以看下面兩位大神的文章,建議先看第一篇文章再看第二篇。
這篇通俗易懂的講解了iOS中的內(nèi)存對齊的應(yīng)用
這篇很好的講了內(nèi)存對齊的定義
先看了第一篇才能很好的理解第二篇文章中內(nèi)存對齊的定義
怎么計(jì)算出NSObject最少內(nèi)存為8,分配內(nèi)存為16呢?
因?yàn)镹SObject在c++中實(shí)際為一個(gè)結(jié)構(gòu)體
struct NSObject_IMPL {
Class isa;
};
在NSObject_IMPL結(jié)構(gòu)體中包含了一個(gè)isa指針,指針在64位的環(huán)境下為8個(gè)字節(jié)。結(jié)構(gòu)體的內(nèi)存大小所其成員所決定,又因?yàn)?code>oc中對象存取是以8字節(jié)來對齊的,NSObject_IMPL結(jié)構(gòu)體成員大小為8,剛好為8的整數(shù)倍不需要補(bǔ)齊,所以NSObject最少內(nèi)存為8。因?yàn)?code>oc對象開辟空間是以16字節(jié)對齊的,NSObject的內(nèi)存為8,不是16的整數(shù)倍,需要補(bǔ)齊是內(nèi)存為16的整數(shù)倍,所以補(bǔ)齊后內(nèi)存就為16了。
Person繼承自NSObject,并有兩個(gè)成員變量,那么Person的最少內(nèi)存和實(shí)際分配內(nèi)存分別是多少呢?
@interface Person : NSObject
{
int _age;
NSString * _name;
}
將Person轉(zhuǎn)化為c++后
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
NSString *_name;
};
(本文章所說的內(nèi)存都是在64位環(huán)境下的)NSObject_IMPL里是一個(gè)isa指針,為8字節(jié),_age為4字節(jié),_name為8字節(jié)。8+4+8=20,又因?yàn)?code>oc中對象存取是以8字節(jié)來對齊的所以Person最少內(nèi)存為24。因?yàn)?code>oc對象開辟空間是以16字節(jié)對齊的所以實(shí)際分配內(nèi)存為32。
需要注意的是蘋果為了節(jié)省內(nèi)存空間對內(nèi)存做了重排,所以在分配內(nèi)存時(shí),并不是按你的成員變量書寫順序去分配的
4、oc對象的分類
oc中的對象主要分為3類分別為:instance對象(實(shí)例對象)、class對象(類對象)、meta-class對象(元類對象)。
1、instance對象(實(shí)例對象)
通過alloc出來的對象就是實(shí)例對象,每次alloc都會生成一個(gè)實(shí)例對象。
NSObject * obj1 = [[NSObject alloc] init];
NSObject * obj2 = [[NSObject alloc] init];
NSLog(@"obj1:%p obj2:%p",obj1,obj2);
上面obj1和obj2就是兩個(gè)不同實(shí)例對象,打印出內(nèi)存地址分別是
obj1:0x10070a740 obj2:0x10070a750
內(nèi)存地址不同就說明了是兩個(gè)不同的對象,分別占據(jù)著兩塊不同的內(nèi)存。
在上面對象本質(zhì)的一部分,我們看到了實(shí)例對象在內(nèi)存中存儲了isa指針和成員變量。
2、class對象(類對象)
每個(gè)類在內(nèi)存中有且只有一個(gè)class對象
NSObject * obj1 = [[NSObject alloc] init];
NSObject * obj2 = [[NSObject alloc] init];
Class objClass1 = [obj1 class];
Class objClass2 = [obj2 class];
NSLog(@"objClass1:%p objClass2:%p",objClass1,objClass2);
objClass1和objClass2都是類對象,打印出來的結(jié)果為
objClass1:0x7fff80670388 objClass2:0x7fff80670388
打印出來的地址是一樣的,是同一塊內(nèi)存,所以是同一個(gè)class對象。
class對象在內(nèi)存中存儲的信息主要包括:isa指針、類的屬性信息(@property)、類的對象方法信息(instance method)、類的協(xié)議信息(protocol)、類的成員變量(ivar)等。
3、meta-class對象(元類對象)
每個(gè)類在內(nèi)存中有且只有一個(gè)meta-class對象,meta-class對象和class對象的內(nèi)存結(jié)構(gòu)是一樣的,但是用途不一樣,在內(nèi)存中存儲的信息主要包括isa指針、superclass指針、類的類方法信息(class method)
獲取元類對象需要用到runtime
Class metaClass = object_getClass([NSObject class]);
需要注意的是通過下面方法獲取到的不是元類對象,而是類對象
Class objClass = [[NSObject class] class];
判斷一個(gè)對象是否為元類對象,可以通過下面的方法(也是runtime中的方法)
BOOL result = class_isMetaClass([NSObject class]);
5、isa指針
由上面的知識點(diǎn),我們已經(jīng)知道在實(shí)例對象、類對象和元類對象中都有著一個(gè)isa指針,isa指針有什么用呢?我們先看下下面1-3這張圖

instance的isa指向class
當(dāng)調(diào)用對象方法時(shí),因?yàn)閷ο蠓椒ㄊ欠旁?code>class對象中的,所以instance對象會通過自己的isa指針找到class,最后找到對象方法的實(shí)現(xiàn)進(jìn)行調(diào)用。class的isa指向meta-class
當(dāng)調(diào)用類方法時(shí),因?yàn)轭惙椒ㄊ欠旁谠獙ο笾械?,所以類對象會先通過本身的isa指針找到meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用。meta-class的isa指向基類的meta-calss
這里需要注意的是meta-class的isa指向基類的meta-calss,而不是父類的meta-calss。
例如有A、B、C、D四個(gè)類,A是基類,D繼承C,C繼承B,B繼承A,那A、B、C、D四個(gè)類的meta-class的isa分別指向誰呢?
答案是都指向A的meta-class,因?yàn)?code>meta-class的isa指向基類的meta-class,A是B、C、D的基類,因?yàn)锳本身就是基類,所以A的meta-calss的isa指向自己的meta-class。-
從64bit開始,
isa需要進(jìn)行一次位運(yùn)算,才能計(jì)算出真實(shí)地址
1-4.png
6、superclass指針
-
superclass指針指向父類,class的superclass指向class的父類,meta-class的superclass指向meta-class的父類 -
class的superclass指向父類的class,如果沒有父類,superclass制作為nil -
meta-class的superclass指向父類的meta-class,基類的meta-class的superclass指向基類的class -
instance調(diào)用對象方法的軌跡:isa找到class,方法不存在,就通過superclass找父類,從父類的方法列表中找 -
class調(diào)用類方法的軌跡:isa找meta-class,方法不存在,就通過superclass找父類,從父類的方法列表中找
從1-4圖中可以看到,instance中沒有superclass指針,class和meta-class中都有superclass指針。
為什么instance中沒有superclass制作呢,因?yàn)?code>instance中已經(jīng)包含了父類的成員變量,所以根本不需要superclass指針再去指向父類獲取成員變量了。例如創(chuàng)建一個(gè)Student類繼承自Person,那么Student的實(shí)例對象底層就是這樣的。
struct Student_IMPL {
struct Person_IMPL Person_IVARS;
int _height;
};
對這結(jié)構(gòu)不是很明白的可以滑到文章上面再看下oc底層實(shí)現(xiàn)這一塊的知識點(diǎn)。
class對象的superclass指針
Person是Student的父類
當(dāng)Student的instance對象要調(diào)用Person的對象方法時(shí),會先通過isa找到Student的class,然后通過superclass找到Person的class,最后找到對象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
meta-class對象的superclass指針
Person是Student的父類
當(dāng)Student的class要調(diào)用Person的類方法時(shí),會先通過isa找到Student的meta-class,然后通過superclass找到Person的meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用
1-5是一張非常經(jīng)典的圖大家可以看下


