1. Objective-C的本質(zhì)
我們平時(shí)編寫的OC代碼,其實(shí)底層實(shí)現(xiàn)都是C/C++代碼,類主要是基于C/C++的結(jié)構(gòu)體的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,因?yàn)閷?duì)象或者類有各種類型(NSArray *沛硅,NSDictionary *,CFfloat
等),因?yàn)榭梢源鎯?chǔ)不同種類的數(shù)據(jù),能夠滿足的這樣的結(jié)構(gòu)就是結(jié)構(gòu)體.
為了證明OC的結(jié)構(gòu),所以可以轉(zhuǎn)換成C++的代碼,窺探內(nèi)部的結(jié)構(gòu)(有時(shí)候C++的代碼也不一定能完全表示源碼的情況,需要調(diào)試到匯編代碼或源碼查看).
我們可以通過終端進(jìn)入到要窺探所在文件的位置,使用命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
.(如果電腦上面安裝了多個(gè)版本的Xcode,轉(zhuǎn)換為C++代碼的時(shí)候會(huì)提示各種框架找不到的錯(cuò)誤,一般是因?yàn)槎鄠€(gè)版本的Xcode路徑?jīng)_突導(dǎo)致的,我們需要在終端指定一個(gè)Xcode的路徑,例:sudo xcode-select --switch/Applications/Xcode10.0.app/Contents/Developer/).
注釋:解釋各種參數(shù)的翻譯
xc就是Xcode的縮寫名惩。
xcrun是Xcode的一種工具褐荷。
-sdk iphoneos規(guī)定sdk需要運(yùn)行在iOS系統(tǒng)上面。
clang是Xcode內(nèi)置的llvm編譯器前端台汇,也是編譯器的一種甲捏。
-arch xxx(arm64、i386景描、armv7...)指出iOS設(shè)備的架構(gòu)囱桨。
參數(shù) -rewrite-objc xx.m 是重寫objc代碼的指令(即重寫xx.m文件) 仓犬。
-o newFileName.cpp 表示輸出新的.cpp文件。
2. NSObject底層實(shí)現(xiàn)原理
Class 定義為 :typedef struct objc_class *Class
;也就是說Class是個(gè)結(jié)構(gòu)體指針.
代碼中[NSObject alloc]
開辟空間給NSObject
舍肠。obj的指針指向了isa的地址.isa的地址就是結(jié)構(gòu)體的地址,原因是結(jié)構(gòu)體的地址就是結(jié)構(gòu)體中第一個(gè)成員的地址,而結(jié)構(gòu)體只有一個(gè)成員,即isa指針的地址.
一. 例:student底層的原理
答:因?yàn)?code>Student繼承NSObject
,也就繼承了NSObject
的數(shù)據(jù)結(jié)構(gòu),所以繼承NSObject
的8個(gè)字節(jié),也就是NSobject
中的isa的大小搀继。
Person占據(jù)class_getInstanceSize=16 malloc_size=16
, Student占據(jù)class_getInstanceSize=16 malloc_size=16
,Person的變量實(shí)際用了12,但是由于內(nèi)存對(duì)齊所以占用16.
二. 兩種方法看內(nèi)存大小
我們有這種方法在OC中表達(dá)一個(gè)類內(nèi)存的大小.
<objc/runtime.h>文件提供class_getInstanceSize(Class _Nullable cls)方法,返回我們一個(gè)OC對(duì)象
的實(shí)例所占用的內(nèi)存大小(可以說是結(jié)構(gòu)體內(nèi)存對(duì)齊之后的大小,8的倍數(shù))翠语;
<malloc/malloc.h>文件提供 size_t malloc_size(const void *ptr)方法返回系統(tǒng)為這個(gè)對(duì)象分配的
內(nèi)存大小(16的倍數(shù))叽躯。
三. 內(nèi)存對(duì)齊的原理(不全,后期添加)
我們先來看一些內(nèi)存的例子,更加方便我們?nèi)ダ斫鈨?nèi)存分配和內(nèi)存對(duì)齊原理:
- 看一個(gè)沒有成員變量的類的實(shí)例(以NSObject為例)
NSObject *obj = [[NSObject alloc] init];
NSLog(@"NSObject實(shí)例大小--> %zd",class_getInstanceSize([obj class]));
NSLog(@"obj實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)obj));
// NSObject實(shí)例大小--> 8
// obj實(shí)際分配的內(nèi)存16
- 一個(gè)普通的類的實(shí)例肌括,并且實(shí)例有且僅有唯一的成員變量(如:Student只有一個(gè)name屬性)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@end;
@implementation Student
@end;
Student *stu = [[Student alloc] init];
stu.name = @"Object-C";
NSLog(@"Student實(shí)例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)stu));
// Student實(shí)例大小--> 16
// stu實(shí)際分配的內(nèi)存16
- 一個(gè)普通的類的實(shí)例点骑,并且實(shí)例有自己的成員變量(如:Student類,為其添加屬性age们童、name等)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end;
@implementation Student
@end;
Student *stu = [Student new];
stu.name = @"Object-C";
stu.age = 25;
NSLog(@"Student實(shí)例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)stu));
// Student實(shí)例大小-->24
// stu實(shí)際分配的內(nèi)存32
由以上三次測(cè)試:一個(gè)OC對(duì)象所占用的內(nèi)存取決于這個(gè)對(duì)象成員變量的多少.但是同時(shí),系統(tǒng)為其分配內(nèi)存時(shí),默認(rèn)會(huì)分配最少16個(gè)字節(jié)的大小.OC中對(duì)象的內(nèi)存小于16就等于16(是Core Foundation的規(guī)定),下面是Core Foundation的源碼.
size_t instanceSize(size_t extraBytes){
size_t size = alignedInstanceSize()+extraBytes;
//CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
內(nèi)存對(duì)齊的原則:結(jié)構(gòu)體的大小必須是最大成員的倍數(shù).
更多的內(nèi)存對(duì)齊的知識(shí)--內(nèi)存對(duì)齊
補(bǔ)充:sizeof不是個(gè)函數(shù)是個(gè)運(yùn)算符,傳入的時(shí)候是類型不是具體的對(duì)象,sizeof是在編譯的時(shí)候進(jìn)行計(jì)算的.
3. OC對(duì)象的分類
objective-C的對(duì)象,簡(jiǎn)稱為OC對(duì)象,分為三種:
- instance對(duì)象(實(shí)例對(duì)象).
- class對(duì)象(類對(duì)象).
- meta-class(元類對(duì)象).
一. 實(shí)例對(duì)象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
object1畔况、object2是NSObject的instance對(duì)象(實(shí)例對(duì)象),它們是不同的兩個(gè)對(duì)象鲸鹦,分別占據(jù)著兩塊不同的內(nèi)存慧库。instance對(duì)象是通過類alloc出來的對(duì)象,每次調(diào)用alloc都會(huì)產(chǎn)生新的instance對(duì)象.instance對(duì)象在內(nèi)存中存儲(chǔ)的信息包括:isa指針,其他成員變量馋嗜。
實(shí)例對(duì)象存放的內(nèi)容包含:
實(shí)例對(duì)象 |
---|
isa |
成員變量信息 |
二. 類對(duì)象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);//Runtime API
Class objectClass5 = object_getClass(object2);//Runtime API
objectClass1 ~ objectClass5都是NSObject的class對(duì)象(類對(duì)象).它們是同一個(gè)對(duì)象,每個(gè)類在內(nèi)存中有且只有一個(gè)class對(duì)象.
類對(duì)象存放的內(nèi)容包含:
類對(duì)象 |
---|
isa |
superclass |
屬性信息 |
對(duì)象方法信息 |
協(xié)議信息 |
成員變量信息 |
............. |
class對(duì)象在內(nèi)存中存儲(chǔ)的信息主要包括:isa指針,superclass指針,類的屬性信息(@property)齐板、類的對(duì)象方法信息(instance method),類的協(xié)議信息(protocol)、類的成員變量信息(ivar).
三. 元類對(duì)象
獲取一個(gè)類對(duì)象的元類對(duì)象的方法.
Class objectMetaClass = object_getClass([NSObject class]);//Runtime API
元類對(duì)象 |
---|
isa |
superclass |
類方法信息 |
............. |
objectMetaClass
是NSObject的meta-class
對(duì)象(元類對(duì)象).每個(gè)類在內(nèi)存中有且只有一個(gè)meta-class對(duì)象.
meta-class對(duì)象和class對(duì)象的內(nèi)存結(jié)構(gòu)是一樣的葛菇,但是用途不一樣甘磨,在內(nèi)存中存儲(chǔ)的信息主要包括:isa指針,superclass指針,類的類方法信息(class method).
補(bǔ)充:
查看Class是否為meta-class:
BOOL result = class_isMetaClass([NSObject class]);
以下代碼獲取的objectClass是class對(duì)象,并不是meta-class對(duì)象
Class objectClass = [[NSObject class] class];
objcget-Class和object-getClass區(qū)別
objc_getClass | 傳入字符串類名返回類對(duì)象. | 傳入字符串類名返回類對(duì)象. | 傳入字符串類名返回類對(duì)象. |
---|---|---|---|
object_getClass | 傳入實(shí)例對(duì)象返回類對(duì)象. | 傳入類對(duì)象返回元類對(duì)象. | 傳入元類對(duì)象返回還是元類對(duì)象 |
四. isa和superClass
1. isa
①instance的isa指向class,當(dāng)調(diào)用對(duì)象方法時(shí)眯停,通過instance的isa找到class济舆,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用.
②class的isa指向meta-class,當(dāng)調(diào)用類方法時(shí),通過class的isa找到meta-class莺债,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用.
2. superClass
當(dāng)Student的instance對(duì)象要調(diào)用Person的對(duì)象方法時(shí)滋觉,會(huì)先通過isa找到Student的class,然后通過superclass找到Person的class齐邦,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用.
當(dāng)Student的class要調(diào)用Person的類方法時(shí)椎侠,會(huì)先通過isa找到Student的meta-class,然后通過superclass找到Person的meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用.
3. 經(jīng)典的isa和superclass圖譜
- isa總結(jié)
- instance的isa都是指向class.
- class的isa都是指向meta-class.
- meta-class的isa指向基類的meta-class.
- superClass總結(jié)
- 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找父類.
- 基類的meta-class方法不存在,就通過superclass找基類的class,如果沒有找到就是nil.
4. isa地址運(yùn)算
從64bit開始辅肾,isa需要進(jìn)行一次位運(yùn)算盾戴,才能計(jì)算出真實(shí)地址,superClass存儲(chǔ)的地址值,直接就是父類的地址值,不用做位運(yùn)算.
實(shí)例對(duì)象里只有成員變量沒有方法,為什么實(shí)例對(duì)象的方法要存在類對(duì)象里,原因是只要存一份就夠了,實(shí)例對(duì)象會(huì)創(chuàng)建多個(gè).
想了解更多iOS學(xué)習(xí)知識(shí)請(qǐng)聯(lián)系:QQ(814299221)