前言
OC是一個(gè)面向?qū)ο蟮木幊陶Z(yǔ)言东涡,對(duì)象
就是我們整個(gè)編寫(xiě)代碼的過(guò)程中,最為頻繁接觸到的一個(gè)東西,那么什么是對(duì)象呢霍弹?在上一篇文章iOS底層 - 結(jié)構(gòu)體內(nèi)存對(duì)齊中,我們了解到:對(duì)象的本質(zhì)就是結(jié)構(gòu)體
娃弓。那么這個(gè)結(jié)論怎么驗(yàn)證呢典格?那么下面開(kāi)始一探究竟。
一.了解clang
在探究對(duì)象本質(zhì)之前先介紹一下clang:
- Clang是?個(gè)C語(yǔ)?台丛、C++耍缴、Objective-C語(yǔ)?的輕量級(jí)編譯器
- Clang將?持其普通lambda表達(dá)式、返回類(lèi)型的簡(jiǎn)化處理以及更好的處理constexpr關(guān)鍵字
- Clang是?個(gè)由Apple主導(dǎo)編寫(xiě)挽霉,基于LLVM的C/C++/Objective-C編譯器
- 2013年4?,Clang已經(jīng)全??持C++11標(biāo)準(zhǔn)防嗡,并開(kāi)始實(shí)現(xiàn)C++1y特性(也就是C++14,這是C++的下?個(gè)?更新版本)
Clang是?個(gè)C++編寫(xiě)侠坎、基于LLVM蚁趁、發(fā)布于LLVM BSD許可證下的C/C++/Objective-C/Objective-C++編譯器。
二.編譯oc文件為c++文件
- 直接命令行編譯 :
// 把?標(biāo)?件編譯成c+
clang -rewrite-objc main.m -o main.cpp
- xcode安裝的時(shí)候順帶安裝了xcrun命令实胸,xcrun命令在clang的基礎(chǔ)上進(jìn)?了?些封裝他嫡,要更好??些:
// 模擬器
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite- objc main.m -o main-arm64.cpp
// 真機(jī)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main arm64.cpp
-
成功生成如下文件:
三.分析c++文件
開(kāi)發(fā)main.cpp文件,搜索LGPerson
:
上圖所示庐完,我們可以看到對(duì)象在底層的本質(zhì)是一個(gè)結(jié)構(gòu)體钢属。
跟蹤NSObject_IMPL結(jié)構(gòu)類(lèi)型可以看到如圖下:
上圖所示,可以得出objc底層調(diào)用就是objc_object
- id class = [class new] 為什么我們id 類(lèi)型可以獲取所有的屬性類(lèi)型而且不需要加门躯,因?yàn)樗牡讓泳褪莍d
@property (nonatomic, strong) NSString *KCName;
- 為什么屬性自帶set 和get 方法 根據(jù)底層跟蹤如下:
// @implementation LGPerson
// 方法 getter
static NSString * _I_LGPerson_kcName(LGPerson * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName))
}
static void _I_LGPerson_setKcName_(LGPerson * self, SEL _cmd, NSString *kcName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName)) = kcName; }
// @end
- 總結(jié):對(duì)象本質(zhì)就是一個(gè)結(jié)構(gòu)體淆党,屬性成員變量實(shí)現(xiàn)了 get 和 set 方法.
- 流程分析 :
LGPerson
—> 找到LGPerson_IMPL
—>NSObject_IMPL
-—>Class
四.isa分析
struct NSObject_IMPL {
Class isa;
};
在上面的代碼里,我們可以看到NSObject里面只有一個(gè)成員變量,那就是Class類(lèi)型的isa宁否。那么這isa是什么呢窒升,我們就這個(gè)問(wèn)題,繼續(xù)探索下去慕匠,首先我們先看看他的類(lèi)型Class在底層中的定義饱须。
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
可以看到,Class實(shí)際上就是一個(gè)objc_class *類(lèi)型的結(jié)構(gòu)體指針台谊。
接下來(lái)我們來(lái)看看isa
蓉媳,在alloc
流程的最后一步,就是通過(guò)initIsa
方法將我們申請(qǐng)的內(nèi)存地址和我們的Class
綁定起來(lái)锅铅。
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
isa = newisa;
}
在這些代碼中間酪呻,有個(gè)非常重要的東西,就是isa_t盐须,我們?cè)賮?lái)看看它到底是什么:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
五.結(jié)構(gòu)體和聯(lián)合體
在上面的代碼中玩荠,我們可以看到一個(gè)之前沒(méi)有接觸過(guò)的結(jié)構(gòu)union,我們稱之為聯(lián)合體贼邓,那么他到底是什么阶冈,有什么特性呢,我們用下面這個(gè)例子來(lái)說(shuō)明塑径,首先看一段代碼
// 結(jié)構(gòu)體 : 共存
struct XHTeacher1 {
char *name;
int age;
double height ;
};
// 聯(lián)合體 : 互斥
union XHTeacher2 {
char *name;
int age;
double height ;
};
結(jié)構(gòu)體(sturct)中的所有變量是“共存”的
優(yōu)點(diǎn):海納百川女坑,有容乃大。只要你來(lái)统舀,我都給你存下來(lái)
缺點(diǎn):內(nèi)存空間的分配是粗放的匆骗,不管你用不用全都給你分配好位置聯(lián)合體(union)中每個(gè)變量之間是“互斥”的
優(yōu)點(diǎn):就是不夠“包容”
缺點(diǎn):使用內(nèi)存更為精細(xì)靈活,也節(jié)省了內(nèi)存空間