舉個??

我們使用clang命令轉(zhuǎn)成c++文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
通過上述命令獲取 Test1 Test2 相關(guān)的源碼:
初探


上圖代碼看出:
OC對象在底層的本質(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類實際上是struct objc_class *結(jié)構(gòu)體指針類型,Class其實就是個別名-
id實際上是struct objc_object *結(jié)構(gòu)體指針類型, 這就是為什么我們平常定義id類型不帶*原因
-
NSObject_IMPL內(nèi)部只有一個成員變量isa

@property屬性在底層取消了屬性而是轉(zhuǎn)換成"下劃線+成員變量"以及set, get方法的形式
@ interface{}成員變量在底層還是以成員變量的存放, 不會有set, get方法
struct NSObject_IMPL NSObject_IVARS其實就是ISA結(jié)構(gòu)體是可以繼承的, 在C++是可以繼承的, 在C可以偽繼承。這種繼承的方式是直接將NSObject結(jié)構(gòu)體定義為 Test1中的第一個成員變量, 意味著 Test1 擁有 NSObject中的所有成員變量。所以
NSObject_IVARS等效于NSObject中的isa同理,
Test2中的第一個成員變量是struct Test1_IMPL Test1_IVARS;OC對象在底層被C語言編程成結(jié)構(gòu)體。而C語言結(jié)構(gòu)體的繼承方式,是每一個結(jié)構(gòu)體第一個屬性都包含父結(jié)構(gòu)體的所有信息。如此,實現(xiàn)了OC類的繼承關(guān)系
所以, Test2的結(jié)構(gòu)簡化后是:
struct Test2_IMPL {
Class isa;
NSString *ivar_name;
NSString *_name;
NSString *_name0;
};
Getter和Setter


偏移量定義


通過上述代碼有可看出:
NSString *_name; NSString *_name0;是自定義的屬性, 在底層是以成員變量的形式存放的定義的屬性,底層自動添加了 get 和 set 方法。get 方法名為
_I_類名_屬性名,set 方法名為_I_類名_set屬性名_,例如:_I_Test1_name_I_Test1_setName_就是他們的getset方法Test1 * self, SEL _cmd,這些是隱藏參數(shù),只存在于底層get方法取值return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObj$_SAName));, 實際上也是一種平移取值方法,self(首地址) + OBJC_IVAR_$_TestObj$_SAName(偏移量) = 想要獲取值的地址, 然后通過直接訪問指針地址,返回指針地址的值set方法則是通過調(diào)用objc_setProperty
什么是對象
對象: 所有繼承于objc_object的都叫做對象實例對象: 即是類對象objc_class實例化出來的isa: 所有對象都有一個isa,指向它的類或元類,里面存儲這類或元類的信息
struct objc_object

對象類型的底層結(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)存位移獲取到了實例對象
m的類對象Man

- 根據(jù)類對象
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)控制,由編譯器自動完成,不受我們管理
實例對象的isa來自于類,類也是對象。那類的isa就指向元類
類既然是對象,就需要管理方法、屬性的存儲和歸屬。而這個管理者,就是元類(Meta)
- 通過獲取
Man類的isa,打印出來還是Man,即是Man的元類

繼續(xù)打印
Man的isa,獲取到Man的根元類NSObject: 0x00000001003720f0,根元類
NSObject的isa則指向自己0x00000001003720f0

通過直接打印
NSObject類地址,發(fā)現(xiàn)與上圖中的不一致而且
NSObject的isa也指向上圖的NSObject所以上圖的
NSObject: 0x00000001003720f0是根元類

- 按照上面的方法,獲取根元類
0x00000001003720f0的父類

可以看到,根元類的父類
isa剛好指向NSObject本類0x0000000100372140
- 獲取
Man元類的父類數(shù)據(jù)

可以看出,
Man元類的父類是Person的元類
以上得出結(jié)論如下圖:

Set方法

set方法最終會走到reallySetProperty做處理

objc_setProperty相當于一個承上啟下接口, 上層許多個set方法直接對接llvm, 則llvm需要針對每一個set做對應(yīng)處理, 則會很麻煩。所以蘋果設(shè)置了一個中間層(接口隔離層)objc_setProperty, 令他處理一部分set方法, 保證無論上層怎么變, 傳入llvm的格式不會有變化。主要是達到上下層接口隔離的目的
- 外部set方法: 個性化定制層(例如setName、setAge等)
↓
- objc_setProperty:接口隔離層 (將外界信息轉(zhuǎn)化為對內(nèi)存地址和值的操作)
↓
- reallySetProperty:底層實現(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會調(diào)用GetPropertySetFunction
其中UseOptimizedSetter(CGM)這個判斷后面標注為// 10.8 and iOS 6.0 code and GC is off即10.8和iOS 6.0代碼,GC關(guān)閉, 所以新版本直接走GetPropertySetFunction
- 查詢GetSetProperty


IsCopy: 如果屬性修飾符為copy, 直接會調(diào)用objc_setProperty
Retain & !IsAtomic: 如果屬性修飾符為retain, 并且為非原子性nonatomic, 也會調(diào)用objc_setProperty
- 驗證:


可看到 copy或者 retain&nonatomic,會調(diào)用objc_setProperty
assgin的話

直接進行偏移后賦值操作
retain和copy都是調(diào)用了objc_setProperty。 不同的是objc_setProperty內(nèi)部實現(xiàn)不同(詳看objc4源碼中的objc_setProperty代碼)
copy和mutableCopy:是新開辟空間,舊值release;
其他修飾類型:是新值retain,舊值release。
strong、assign類型都是直接使用地址進行賦值(通過對象地址偏移相應(yīng)字節(jié)找到屬性地址)
如果在set方法后加入斷點,可以在匯編層看到所有屬性賦值后,會調(diào)用objc_storeStrong。
ARC下, retain和strong都走這里:

先
retain新值,然后賦值再將舊值release
探索


+ (id)self {
/// 當前類對象
return (id)self;
}
- (id)self {
/// 返回當前實例
return self;
}
+ (Class)class {
/// 返回當前類
return self;
}
- (Class)class {
/// 當前實例對象的類
return object_getClass(self);
}
+ (Class)superclass {
/// 返回當前類的父類
return self->getSuperclass();
}
- (Class)superclass {
/// 返回當前類的父類
return [self class]->getSuperclass();
}
+ (BOOL)isMemberOfClass:(Class)cls {
/// 當前類的元類是否與傳入的類相等
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
/// 當前實例對象的類是否與傳入的類相等
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的底層實現(xiàn):
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
// 做了一步obj->getIsa() 獲取isa, 即
// 如果obj是對象,則isa是類,
// 如果obj是類,則isa是元類
Class cls = obj->getIsa();
// 緩存中是否能查找到當前類的isKindOfClass方法
// 找到走if
if (fastpath(!cls->hasCustomCore())) {
// 循環(huán)判斷
// 如果是對象
// 當前類 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);
}

