舉個(gè)??
我們使用clang
命令轉(zhuǎn)成c++
文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
通過上述命令獲取 Test1
Test2
相關(guān)的源碼
初探
上圖代碼看出:
- OC對(duì)象在底層的本質(zhì)就是結(jié)構(gòu)體,結(jié)構(gòu)體中包含了所有屬性方法和父類信息
-
typedef struct objc_object Test1
, 可看出Test1
是objc_object
類型的結(jié)構(gòu)體
typedef struct objc_class *Class;
typedef struct objc_object *id;
可看出:
Class
類實(shí)際上是struct objc_class *
結(jié)構(gòu)體指針類型,Class
其實(shí)就是個(gè)別名-
id
實(shí)際上是struct objc_object *
結(jié)構(gòu)體指針類型, 這就是為什么我們平常定義id
類型不帶*
原因
NSObject_IMPL
內(nèi)部只有一個(gè)成員變量isa
- @property屬性在底層取消了屬性而是轉(zhuǎn)換成"下劃線+成員變量"以及set, get方法的形式
- @ interface{}成員變量在底層還是以成員變量的存放, 不會(huì)有set, get方法
-
struct NSObject_IMPL NSObject_IVARS
其實(shí)就是ISA
- 結(jié)構(gòu)體是可以繼承的, 在C++是可以繼承的, 在C可以偽繼承。這種繼承的方式是直接將NSObject結(jié)構(gòu)體定義為 Test1中的第一個(gè)成員變量, 意味著 Test1 擁有 NSObject中的所有成員變量。所以
NSObject_IVARS
等效于NSObject
中的isa
- 同理,
Test2
中的第一個(gè)成員變量是struct Test1_IMPL Test1_IVARS;
- OC對(duì)象在底層被C語言編程成結(jié)構(gòu)體乎完。而C語言結(jié)構(gòu)體的繼承方式摩桶,是每一個(gè)結(jié)構(gòu)體第一個(gè)屬性都包含父結(jié)構(gòu)體的所有信息。如此,實(shí)現(xiàn)了OC類的繼承關(guān)系
所以, Test2
的結(jié)構(gòu)簡(jiǎn)化后是:
struct Test2_IMPL {
Class isa;
NSString *ivar_name;
NSString *_name;
NSString *_name0;
};
Getter
和Setter
偏移量定義
通過上述代碼有可看出:
-
NSString *_name; NSString *_name0;
是自定義的屬性, 在底層是以成員變量的形式存放的 - 定義的屬性,底層自動(dòng)添加了 get 和 set 方法。get 方法名為
_I_類名_屬性名
,set 方法名為_I_類名_set屬性名_
,例如:_I_Test1_name
_I_Test1_setName_
就是他們的get
set
方法 -
Test1 * self, SEL _cmd
,這些是隱藏參數(shù),只存在于底層 -
get
方法取值return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObj$_SAName));
, 實(shí)際上也是一種平移取值方法,self(首地址) + OBJC_IVAR_$_TestObj$_SAName(偏移量) = 想要獲取值的地址
, 然后通過直接訪問指針地址串前,返回指針地址的值 -
set
方法則是通過調(diào)用objc_setProperty
什么是對(duì)象
-
對(duì)象
: 所有繼承于objc_object
的都叫做對(duì)象 -
實(shí)例對(duì)象
: 即是類對(duì)象objc_class
實(shí)例化出來的 -
isa
: 所有對(duì)象都有一個(gè)isa
,指向它的類或元類,里面存儲(chǔ)這類或元類的信息
struct objc_object
對(duì)象類型的底層結(jié)構(gòu),首元素是isa
(這里涉及到struct結(jié)構(gòu)的內(nèi)存優(yōu)化拨脉,我們這里記住結(jié)論。isa在objc_object的首位元素即可)
struct objc_class
從上圖的結(jié)構(gòu)可以看出,在類的內(nèi)存中货抄,首地址表示isa
,superclass
在isa
后面凉蜂,需要內(nèi)存地址偏移8位獲取
深入探索
示例:
尋找父類
- 通過內(nèi)存位移獲取到了實(shí)例對(duì)象
m
的類對(duì)象Man
-
根據(jù)類對(duì)象
objc_class
結(jié)構(gòu),可以看出superclass
在isa
之后,isa
占8位,所以通過類地址偏移8位獲取到父類Person
先獲取到
Person
的isa
是0x00000001000089d8
-
然后內(nèi)存偏移8位,獲取到它的父類
NSObject
-
同上繼續(xù)查找
NSObjet
的父類是nil(0x0000000000000000)
over!
isa溯源
元類
的定義和創(chuàng)建都由系統(tǒng)控制,由編譯器自動(dòng)完成简十,不受我們管理
實(shí)例對(duì)象的isa
來自于類橙喘,類也是對(duì)象。那類的isa
就指向元類
類既然是對(duì)象半沽,就需要管理方法、屬性的存儲(chǔ)和歸屬。而這個(gè)管理者,就是元類(Meta)
-
通過獲取
Man
類的isa
,打印出來還是Man
,即是Man的元類
繼續(xù)打印
Man
的isa
,獲取到Man
的根元類NSObject: 0x00000001003720f0
,-
根元類
NSObject
的isa
則指向自己0x00000001003720f0
3e92714ded3a0957ba5cb946fefa81bd-65049.png 通過直接打印
NSObject
類地址,發(fā)現(xiàn)與上圖中的不一致而且
NSObject
的isa
也指向上圖的NSObject
-
所以上圖的
NSObject: 0x00000001003720f0
是根元類
-
按照上面的方法,獲取根元類
0x00000001003720f0
的父類
可以看到,根元類的父類
isa
剛好指向NSObject
本類0x0000000100372140
- 獲取
Man
元類的父類數(shù)據(jù)
image.png
可以看出,
Man
元類的父類是Person
的元類
以上得出結(jié)論如下圖:
Set方法
set
方法最終會(huì)走到reallySetProperty
做處理
objc_setProperty
相當(dāng)于一個(gè)承上啟下接口, 上層許多個(gè)set
方法直接對(duì)接llvm, 則llvm需要針對(duì)每一個(gè)set
做對(duì)應(yīng)處理, 則會(huì)很麻煩。所以蘋果設(shè)置了一個(gè)中間層(接口隔離層)objc_setProperty
, 令他處理一部分set
方法, 保證無論上層怎么變, 傳入llvm的格式不會(huì)有變化竣付。主要是達(dá)到上下層接口隔離的目的
- 外部set方法: 個(gè)性化定制層(例如setName颊乘、setAge等)
↓ - objc_setProperty:接口隔離層 (將外界信息轉(zhuǎn)化為對(duì)內(nèi)存地址和值的操作)
↓ - reallySetProperty:底層實(shí)現(xiàn)層 (賦值和內(nèi)存管理)
LLVM
中的objc_setProperty
- 在llvm源碼中搜索
"objc_setProperty"
發(fā)現(xiàn)getSetPropertyFn()方法中調(diào)用CGM.CreateRuntimeFunction(FTy, "objc_setProperty");CGM創(chuàng)建runtime函數(shù)objc_setProperty
Fn: 為function函數(shù)的縮寫
-
全局搜索
getSetPropertyFn()
-
查詢
GetPropertySetFunction
可看到如果case為GetSetProperty和SetPropertyAndExpressionGet會(huì)調(diào)用GetPropertySetFunction
其中UseOptimizedSetter(CGM)這個(gè)判斷后面標(biāo)注為// 10.8 and iOS 6.0 code and GC is off即10.8和iOS 6.0代碼,GC關(guān)閉, 所以新版本直接走GetPropertySetFunction
-
查詢GetSetProperty
IsCopy: 如果屬性修飾符為copy, 直接會(huì)調(diào)用objc_setProperty
Retain & !IsAtomic: 如果屬性修飾符為retain, 并且為非原子性nonatomic, 也會(huì)調(diào)用objc_setProperty
-
驗(yàn)證:
可看到 copy或者 retain&nonatomic,會(huì)調(diào)用objc_setProperty
assign
的話
直接進(jìn)行偏移后賦值操作
- retain和copy都是調(diào)用了objc_setProperty。 不同的是objc_setProperty內(nèi)部實(shí)現(xiàn)不同(詳看objc4源碼中的objc_setProperty代碼)
- copy和mutableCopy:是新開辟空間渠概,舊值release状共;
其他修飾類型:是新值retain,舊值release舶吗。 - strong征冷、assign類型都是直接使用地址進(jìn)行賦值(通過對(duì)象地址偏移相應(yīng)字節(jié)找到屬性地址)
- 如果在set方法后加入斷點(diǎn),可以在匯編層看到所有屬性賦值后誓琼,會(huì)調(diào)用objc_storeStrong检激。
ARC
下, retain和strong都走這里:
[圖片上傳失敗...(image-c94f02-1729845469374)]
先retain
新值,然后賦值再將舊值release
測(cè)試
+ (id)self {
/// 當(dāng)前類對(duì)象
return (id)self;
}
- (id)self {
/// 返回當(dāng)前實(shí)例
return self;
}
+ (Class)class {
/// 返回當(dāng)前類
return self;
}
- (Class)class {
/// 當(dāng)前實(shí)例對(duì)象的類
return object_getClass(self);
}
+ (Class)superclass {
/// 返回當(dāng)前類的父類
return self->getSuperclass();
}
- (Class)superclass {
/// 返回當(dāng)前類的父類
return [self class]->getSuperclass();
}
+ (BOOL)isMemberOfClass:(Class)cls {
/// 當(dāng)前類的元類是否與傳入的類相等
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
/// 當(dāng)前實(shí)例對(duì)象的類是否與傳入的類相等
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
// 循環(huán)判斷
// 元類 vs 傳入類
// 父元類 vs 傳入類
// 根元類 vs 傳入類
// 根類 vs 傳入類
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
// 循環(huán)判斷
// 類 vs 傳入類
// 父類 vs 傳入類
// 根類 vs 傳入類
// nil vs 傳入類
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
isKindOfClass
的底層實(shí)現(xiàn):
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
// 做了一步obj->getIsa() 獲取isa, 即
// 如果obj是對(duì)象,則isa是類腹侣,
// 如果obj是類叔收,則isa是元類
Class cls = obj->getIsa();
// 緩存中是否能查找到當(dāng)前類的isKindOfClass方法
// 找到走if
if (fastpath(!cls->hasCustomCore())) {
// 循環(huán)判斷
// 如果是對(duì)象
// 當(dāng)前類 vs 傳入類
// 父類 vs 傳入類
// 根類 vs 傳入類
// nil vs 傳入類
// 如果是類
// 元類 vs 傳入類
// 父元類 vs 傳入類
// 根元類 vs 傳入類
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
/// 沒找到,直接發(fā)送消息
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}