本文的主要目的是分析 類 & 類的結(jié)構(gòu),整篇都是圍繞一個類
展開的一些探索
1.類的分析之Class isa和Class superclass ---> isa走向和繼承關(guān)系鏈
類的分析 主要是分析 isa
的走向 以及 類之間的繼承
關(guān)系,注意對象之間
沒有繼承關(guān)系
哦
分析類之前提出幾個問題
1.Class
到底是什么?Class
與類
之間的關(guān)系?
2.對象
的isa
和類
的isa
之間的區(qū)別?分別存儲的是什么值?
3.對象方法
存儲在類
中,那類方法
存儲在哪里呢?如果一直有相應(yīng)的類
來存儲那么盡頭在哪里呢?
4.isa
與supperClass
是什么關(guān)系? isa
到底有哪些作用?
5.類
在內(nèi)存中到底存在幾份
?
類的底層定義,runtime.h里早期類的定義,現(xiàn)在已經(jīng)棄用
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; // isa
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE; // super_class 父類指針
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;//協(xié)議列表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
objc_object的部分代碼
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA(bool authenticated = false);
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
uintptr_t isaBits() const;
bool hasNonpointerIsa();
bool isTaggedPointer();
bool isTaggedPointerOrNil();
bool isBasicTaggedPointer();
bool isExtTaggedPointer();
bool isClass();
// object may have associated objects?
bool hasAssociatedObjects();
void setHasAssociatedObjects();
// object may be weakly referenced?
bool isWeaklyReferenced();
void setWeaklyReferenced_nolock();
// object may have -.cxx_destruct implementation?
bool hasCxxDtor();
// Optimized calls to retain/release methods
id retain();
void release();
id autorelease();
// Implementations of retain/release methods
id rootRetain();
bool rootRelease();
id rootAutorelease();
bool rootTryRetain();
bool rootReleaseShouldDealloc();
uintptr_t rootRetainCount();
// Implementation of dealloc methods
bool rootIsDeallocating();
void clearDeallocating();
void rootDealloc();
};
類的新的定義,部分代碼, objc-runtime-new.h文件里面
struct objc_class : objc_object {
// Class ISA; // isa 繼承自objc_object
Class superclass; //父類指針
cache_t cache; // formerly cache pointer and vtable //緩存
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags // 存儲類信息(協(xié)議、方法、成員變量、屬性)
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
}
Class id宏定義
typedef struct objc_class *Class;
typedef struct objc_object *id;
總結(jié):
1.Class
是objc_class
是類
,類
來自于底層objc_class
結(jié)構(gòu)體
2.id
是objc_object
是對象
,即對象
來自于objc_object
結(jié)構(gòu)體
3.objc_class : objc_object
,objc_class
繼承自objc_object
結(jié)構(gòu)體,objc_class
的isa
繼承自objc_object
,所以,類
和對象都有Class isa
指針
4.類
有superclass
有繼承關(guān)系,對象
沒有superclass
沒有繼承關(guān)系
5.本篇博客研究類的 isa、superclass、bits
三個成員變量,很有規(guī)律性,各個源碼版本略有差異
1.準備工作
main.m
里自定義類CJLPerson
,代碼如下
// 協(xié)議 protocol
@protocol CJLPersonDelegate <NSObject>
//實例方法
- (void)sendDataToController:(NSMutableArray *)dataArray;
// 類方法
+ (void)sendString:(NSString *)string;
// 可選實例方法
@optional
- (void)showKeyboard;
// 可選類方法
@optional
+ (void)closeKeyboard;
@end
@interface CJLPerson : NSObject <CJLPersonDelegate> {
// 成員變量
NSString * hobby;
NSString * sex;
}
// 屬性
@property (nonatomic, copy) NSString * cjl_name;
@property (nonatomic, copy) NSString * number;
// 實例方法
- (void)sayHello:(NSString *)hello;
- (void)sayWorld;
// 類方法
+ (void)sayBye;
+ (void)sayLove;
@end
@implementation CJLPerson
- (void)sayHello:(NSString *)hello {
}
- (void)sayWorld {
}
+ (void)sayBye {
}
+ (void)sayLove {
}
- (void)sendDataToController:(NSMutableArray *)dataArray {
}
- (void)showKeyboard {
}
+ (void)sendString:(NSString *)string {
}
調(diào)用方式
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
CJLPerson * person = [[CJLPerson alloc] init];
NSLog(@"person == %@", person);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
@end
2.元類
首先,我們先通過一個案例的lldb
調(diào)試先引入元類
,對象
的isa
指向類
,類
的isa
指向的就是元類
通過斷點調(diào)試,lldb輸出如下,相信說明見注釋
我們由前面的內(nèi)容知道對象的內(nèi)存地址是第一個8字節(jié)是isa的值,其他后面的都是對應(yīng)屬性的值,假設(shè)都是NSString類型的屬性,都是8字節(jié),對象內(nèi)存示意圖如下
對象的內(nèi)存地址和內(nèi)存分布
{
0x0100000104c01418 :Class isa 值 第一個8字節(jié)
0x0100000104c01420 :name 值 第二個8字節(jié)
0x0100000104c01428:height 值 第三個8字節(jié)
0x0100000104c01430:weight 值 第四個8字節(jié)
0x0100000104c01438:XXX 值 依次類推
}
類的源碼結(jié)構(gòu)
struct objc_class : objc_object {
Class ISA; // Class 類型 8字節(jié) 繼承自objc_object, 第一8字節(jié)是isa
Class superclass; // Class 類型 8字節(jié), 第二8字節(jié)是superclass也就是父類
cache_t cache; // 16字節(jié)
class_data_bits_t bits; //
...
}
以上可知
1.對象
的第一個8
字節(jié)是isa
,
2.類
的第一個8
字節(jié)是isa
3.類
的第二個8
字節(jié)是supperclass
,也就是父類
4.對象
沒有supperclass
,也就是說對象
沒有繼承關(guān)系
,只有類
有繼承關(guān)系
(lldb) x/4gx person
0x600003705e40: 0x0100000104c014d9 0x0000000000000000 //0x0100000104c014d9 對象的isa
0x600003705e50: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL // 對象的isa &上ISA_MASK
(unsigned long long) $1 = 0x0000000104c014d8 //類的16進制數(shù)值
(lldb) po 0x0000000104c014d8 //打印類的16進制數(shù)值為CJLPerson
CJLPerson
(lldb) p/x CJLPerson.class //p/x直接打印類的16進制數(shù)值,可以看到與上面的一致
(Class) $3 = 0x0000000104c014d8 CJLPerson
(lldb) x/4gx 0x0000000104c014d8 //打印類的4段內(nèi)存
0x104c014d8: 0x0000000104c014b0 0x00000001c28fc628 //0x0000000104c014b0 類的isa
0x104c014e8: 0x0001600003705e60 0x0001802100000000
(lldb) p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL // 類的isa &上ISA_MASK
(unsigned long long) $4 = 0x0000000104c014b0// 類的isa &上ISA_MASK的值與沒有&上ISA_MASK完全一致
// 說明類的isa 8字節(jié)64位中除了33位類信息之外其他全部為0
(lldb) po 0x0000000104c014b0 // 打印類的isa指向輸出結(jié)果依然是CJLPerson
CJLPerson
//0x0000000104c014d8 CJLPerson,名字相同,16進制數(shù)值不同,很明顯是兩個類,對象isa指向的是類,CJLPerson類本身
//0x0000000104c014b0 CJLPerson,名字相同,16進制數(shù)值不同,很明顯是兩個類,類isa指向的是元類,
//CJLPerson的元類,只是名字相同而已,暫且標記為CJLPerson_Metal
// CJLPerson和CJLPerson的元類(CJLPerson_Metal)是兩個不同的概念
(lldb) x/4gx 0x0000000104c014b0 //打印CJLPerson的元類的4段內(nèi)存
0x104c014b0: 0x00000001c28fc600 0x00000001c28fc600
0x104c014c0: 0x0003600002258080 0x0002e03500000000
(lldb) po 0x00000001c28fc600 //輸出CJLPerson_Metal的isa指向NSObject,此NSObject是NSObject的元類
NSObject // 即NSObject_Metal與NSObject是兩個概念
(lldb) x/4gx 0x00000001c28fc600 // 繼續(xù)輸出NSObject_Metal的isa指向,依然指向自身
0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
0x1c28fc610: 0x00036000022580c0 0x0003e03400000000
(lldb) x/4gx 0x00000001c28fc600 // 繼續(xù)輸出NSObject_Metal的isa指向,依然指向自身
0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
0x1c28fc610: 0x00036000022580c0 0x0003e03400000000
(lldb) p/x NSObject.class //打印NSObject.class類本身
(Class) $7 = 0x00000001c28fc628 NSObject
(lldb)
//0x00000001c28fc628 NSObject
//0x00000001c28fc600 NSObject元類(NSObject_Metal)
// 這兩個是不同的概念,不同的類
define ISA_MASK 0x0000000ffffffff8ULL
根據(jù)調(diào)試過程,我們產(chǎn)生了一個疑問:為什么圖中的p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL
與 p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL
中的類信息打印出來的類名都是CJLPerson
,這兩個CJLPerson
是什么關(guān)系呢?
0x0100000104c014d9
是person
對象的isa
指針地址,其&后得到的結(jié)果是 創(chuàng)建person
的類CJLPerson
0x0000000104c014b0
是CJLPerson
類的isa
的所存儲的指針地址,即 CJLPerson
類的類的地址,在Apple中,我們簡稱CJLPerson類的類為元類
0x0100000104c014d9 & 0x0000000ffffffff8ULL
= 0x0000000104c014d8
(值
或者是首地址
不同) = CJLPerson
,類名相同
,這是CJLPerson類
0x0000000104c014b0 & 0x0000000ffffffff8ULL
= 0x0000000104c014b0
(值
或者是首地址
不同) = CJLPerson
,類名相同
,這是CJLPerson元類
,暫且標記為CJLPerson _metaClass
CJLPerson類
和CJLPerson _metaClass
是兩個不同的類
,且每個
在內(nèi)存中只有一份
,且內(nèi)存地址
不同,
元類
和類
是兩個不同的概念,只是體現(xiàn)出來的類名字
相同而已
所以,兩個打印都是CJLPerson的根本原因就是因為元類導(dǎo)致的
3.元類的說明
下面來解釋什么是元類,主要有以下幾點說明:
我們都知道 對象
的isa
是指向類
,類
其實也是一個對象
,可以稱為類對象
,其isa
指向蘋果定義的元類
元類
是系統(tǒng)
給的,其定義
和創(chuàng)建
都是由編譯器
完成,在這個過程中,類
的歸屬來自于元類
元類
是類對象 的類,每個類
都有一個獨一無二的元類
用來存儲 類方法
的相關(guān)信息。
元類
本身是沒有名稱
的,由于與類
相關(guān)聯(lián),所以使用了同類
名一樣的名稱
下面通過lldb命令來探索元類的走向,也就是isa的走位,如下圖所示,可以得出一個關(guān)系鏈:
對象isa
---> 類isa
---> 元類isa
---> NSobject(根元類)
---> NSobject(根元類)
person
(對象) ---> CJLPerson
(類) ---> CJLPerson _metaClass
(CJLPerson元類) ---> NSobject_metaClass
(NSobject根元類)--->NSobject_metaClass
(根元類自身)
CJLPerson
不是CJLPerson_metaClass
(CJLPerson元類),內(nèi)存地址
不同,但是每一個只有一份
NSobject
不是NSobject_metaClass
(根元類),內(nèi)存地址
不同,但是每一個只有一份
4.isa走位圖
5.LLDB調(diào)試 isa走位圖
6.superclass指向
7.LLDB調(diào)試 superclass指向
8.蘋果官方的isa走位以及繼承關(guān)系圖
9.LLDB調(diào)試 CJLTeacher ---> CJLPerson ---> NSObject 三級繼承關(guān)系isa走位及superclass指向
新增類CJLTeacher
繼承于CJLPerson
, CJLPerson
繼承于NSObject
,調(diào)用如下
@interface CJLTeacher : CJLPerson
@end
@implementation CJLTeacher
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
// ISA_MASK 0x0000000ffffffff8ULL
CJLTeacher * teacher = [[CJLTeacher alloc] init];
CJLPerson * person = [[CJLPerson alloc] init];
NSObject * obj = [[NSObject alloc] init];
NSLog(@"teacher == %@ person == %@ obj == %@", teacher, person, obj);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
LLDB調(diào)試,完美驗證iOS系統(tǒng)的isa走位及繼承關(guān)系圖
10. clang 編譯位cpp文件 查看isa 和 superclass指向
CJLTeacher
以及其元類isa
和superclass
指向
CJLPerson
以及其元類isa
和superclass
指向
NSObject根元類
被編譯成OBJC_METACLASS_$_NSObject
CJLPerson元類
被編譯成OBJC_METACLASS_$_CJLPerson
CJLTeacher元類
被編譯成OBJC_METACLASS_$_CJLTeacher
NSObject
被編譯成OBJC_CLASS_$_NSObject
CJLPerson
被編譯成OBJC_CLASS_$_CJLPerson
static void OBJC_CLASS_SETUP_$_CJLTeacher(void ) {
OBJC_METACLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_CJLTeacher.superclass = &OBJC_METACLASS_$_CJLPerson;
OBJC_METACLASS_$_CJLTeacher.cache = &_objc_empty_cache;
OBJC_CLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_CJLTeacher;
OBJC_CLASS_$_CJLTeacher.superclass = &OBJC_CLASS_$_CJLPerson;
OBJC_CLASS_$_CJLTeacher.cache = &_objc_empty_cache;
}
static void OBJC_CLASS_SETUP_$_CJLPerson(void ) {
OBJC_METACLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_CJLPerson.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_CJLPerson.cache = &_objc_empty_cache;
OBJC_CLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_CJLPerson;
OBJC_CLASS_$_CJLPerson.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_CJLPerson.cache = &_objc_empty_cache;
}
2.類的分析之類信息存儲 ---> bits( class_data_bits_t 結(jié)構(gòu)體類 )
以上研究了類的isa
走勢圖和superclass
繼承關(guān)系圖,那么類的其他信息比如方法、協(xié)議、屬性、成員變量
存在什么地方呢?
cache_t cache
是方法緩存
很明顯類的相關(guān)信息是存在class_data_bits_t bits
這個結(jié)構(gòu)體成員變量里面的
下面探索 bits
里面怎么存儲了方法、協(xié)議、屬性、成員變量
等信息的,各個需要用到的結(jié)構(gòu)體的部分代碼如下,精簡便于理解,不同版本略有差異,但是流程是一樣的
1.objc_class
類結(jié)構(gòu)體部分代碼
struct objc_class : objc_object {
// Class ISA; // --> 繼承自objc_object --> 8字節(jié)
Class superclass; // --> superclass 父類指針--> 8字節(jié)
cache_t cache; // 方法緩存 formerly cache pointer and vtable --> 16字節(jié)
class_data_bits_t bits; // 類信息存儲 class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
}
-
class_data_bits_t
結(jié)構(gòu)體部分代碼
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
public:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
-
class_rw_t
結(jié)構(gòu)體部分代碼
struct class_rw_t {
public:
const class_ro_t *ro() const { //ro() ---> 獲取存儲成員變量的相關(guān)結(jié)構(gòu)體 ---> class_ro_t
auto v = get_ro_or_rwe(); // class_ro_t ---> 獲取成員變量方法列表
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
const method_array_t methods() const { //methods()函數(shù) ---> 獲取方法列表
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const { //properties() ---> 獲取屬性列表
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const { //獲取協(xié)議列表
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
-
method_t
結(jié)構(gòu)體部分代碼
struct method_t {
struct big { // method_t 內(nèi)部又重新定義了一個 struct big 三個成員變量 SEL---types---IMP //同 method_t
SEL name;
const char *types;
MethodListIMP imp; // using MethodListIMP = IMP;
};
public:
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
SEL name() const { // name() ---> 獲取SEL
if (isSmall()) {
return (small().inSharedCache()
? (SEL)small().name.get()
: *(SEL *)small().name.get());
} else {
return big().name;
}
}
const char *types() const { // types() ---> 獲取方法簽名
return isSmall() ? small().types.get() : big().types;
}
IMP imp(bool needsLock) const { // imp(1) ---> 獲取IMP
if (isSmall()) {
IMP imp = remappedImp(needsLock);
if (!imp)
imp = ptrauth_sign_unauthenticated(small().imp.get(),
ptrauth_key_function_pointer, 0);
return imp;
}
return big().imp;
}
};
-
property_t
結(jié)構(gòu)體全部
struct property_t {
const char *name; // 名稱
const char *attributes; // 特性
};
-
class_ro_t
結(jié)構(gòu)體部分代碼
struct class_ro_t {
const ivar_list_t * ivars; // 成員變量列表
}
-
ivar_t
結(jié)構(gòu)體部分代碼
struct ivar_t {
const char *name; //成員變量名稱
const char *type; // 成員變量類型
};
8.protocol_t : objc_object
結(jié)構(gòu)體部分代碼,繼承自objc_object
struct protocol_t : objc_object {
const char *mangledName; // 協(xié)議名稱
struct protocol_list_t *protocols;
method_list_t *instanceMethods; // 實例方法列表
method_list_t *classMethods; // 類方法列表
method_list_t *optionalInstanceMethods;// 可選實例方法列表
method_list_t *optionalClassMethods;// 可選類方法列表
property_list_t *instanceProperties;
uint32_t size; // sizeof(protocol_t)
uint32_t flags;
};
核心思路 :
對應(yīng)的結(jié)構(gòu)體
---> 函數(shù)(解析內(nèi)存結(jié)構(gòu))
---> 獲取到其他結(jié)構(gòu)體
---> 函數(shù)(解析內(nèi)存結(jié)構(gòu))
---> 需要的結(jié)構(gòu)體
---> 函數(shù)
10.實例方法method獲取流程
結(jié)構(gòu)體流程
objc_class(類)
---> class_data_bits_t
---> class_rw_t
--->method_t
函數(shù)流程
data()
---> methods()
---> name()
---> 獲取SEL
data()
---> methods()
---> types()
---> 獲取types 函數(shù)簽名
data()
---> methods()
---> imp(1)
---> 獲取IMP
整體流程
objc_class(類)
---> class_data_bits_t
---> data()
---> class_rw_t
---> methods()
---> method_t
method_t
---> name()
--->SEL
method_t
---> types()
---> 方法簽名
method_t
--- >imp(1)
---> IMP
11.類方法method獲取流程
結(jié)構(gòu)體流程
objc_class(元類)
---> class_data_bits_t
---> class_rw_t
--->method_t
函數(shù)流程
data()
---> methods()
---> name()
---> 獲取SEL
data()
---> methods()
---> types()
---> 獲取types 函數(shù)簽名
data()
---> methods()
---> imp(1)
---> 獲取IMP
整體流程
objc_class(元類)
---> class_data_bits_t
---> data()
---> class_rw_t
---> methods()
---> method_t
method_t
---> name()
--->SEL
method_t
---> types()
---> 方法簽名
method_t
--- >imp(1)
---> IMP
12.屬性property獲取流程
結(jié)構(gòu)體流程
objc_class(類)
---> class_data_bits_t
---> class_rw_t
--->property_t
函數(shù)流程
data()
---> properties()
---> name
---> 獲取屬性名稱
data()
---> properties()
---> attributes
---> 獲取屬性特性attributes
整體流程
objc_class(類)
---> class_data_bits_t
---> data()
---> class_rw_t
---> properties()
---> property_t
property_t
---> name
---> 獲取屬性名稱
property_t
---> attributes
---> 獲取屬性特性attributes
13.成員變量ivar獲取流程
結(jié)構(gòu)體流程
objc_class(類)
---> class_data_bits_t
---> class_rw_t
--->class_ro_t
---> ivar_t
函數(shù)流程
data()
---> ro()
---> ivars
---> name
獲取屬性名稱
data()
---> ro()
---> ivars
---> type
獲取屬性類型
整體流程
objc_class(類)
---> class_data_bits_t
---> data()
---> class_rw_t
--->ro()
---> class_ro_t
---> ivar_t
ivars
---> name
獲取屬性名稱
ivar_t
---> type
---> 獲取屬性類型
14.協(xié)議protocol獲取流程
結(jié)構(gòu)體流程
objc_class(類)
---> class_data_bits_t
---> class_rw_t
--->protocol_t(對象)
---> method_t
函數(shù)流程
data()
---> protocols()
---> instanceMethods
---> 獲取實例方法列表
instanceMethods
---> name()
--->SEL
instanceMethods
---> types()
--->方法簽名
instanceMethods
---> imp(1)
--->IMP
同上
data()
---> protocols()
---> classMethods
---> 獲取類方法列表
data()
---> protocols()
---> optionalInstanceMethods
---> 獲取獲取可選實例方法
data()
---> protocols()
---> optionalClassMethods
---> 獲取獲取可選類方法
整體流程
objc_class(類)
---> class_data_bits_t
---> data()
---> class_rw_t
---> protocols()
---> protocol_t(對象)
---> instanceMethods
instanceMethods
---> name()
--->SEL
instanceMethods
---> types()
--->方法簽名
instanceMethods
---> imp(1)
--->IMP
15.LLDB調(diào)試實例方法method解析
16.LLDB調(diào)試類方法method解析
17.LLDB調(diào)試屬性property解析
18.LLDB調(diào)試成員變量ivar解析
19.LLDB調(diào)試協(xié)議protocol解析
20.LLDB調(diào)試-實例方法instanceMethod調(diào)試過程
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x00000001000081c0 CJLPerson
(lldb) p (class_data_bits_t *) 0x00000001000081e0
(class_data_bits_t *) $1 = 0x00000001000081e0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000101119d10
(lldb) p $2->methods()
(const method_array_t) $3 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100003ea0
}
arrayAndFlag = 4294983328
}
}
}
(lldb) p $3.list
(const method_list_t_authed_ptr<method_list_t>) $4 = {
ptr = 0x0000000100003ea0
}
(lldb) p $4.ptr
(method_list_t *const) $5 = 0x0000000100003ea0
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
}
(lldb) p $6.get(0).name()
(SEL) $7 = "sayHello:"
(lldb) p $6.get(0).types()
(const char *) $8 = 0x0000000100003f7a "v24@0:8@16"
(lldb) p $6.get(0).imp(1)
(IMP) $9 = 0x0000000100003be0 (KCObjcBuild`-[CJLPerson sayHello:] at main.m:34)
(lldb) p $6.get(1).name()
(SEL) $10 = "sayWorld"
(lldb) p $6.get(1).types()
(const char *) $11 = 0x0000000100003f66 "v16@0:8"
(lldb) p $6.get(1).imp(1)
(IMP) $12 = 0x0000000100003c1c (KCObjcBuild`-[CJLPerson sayWorld] at main.m:38)
(lldb) p $6.get(2).name()
(SEL) $13 = "cjl_name"
(lldb) p $6.get(2).types()
(const char *) $14 = 0x0000000100003f85 "@16@0:8"
(lldb) p $6.get(2).imp(1)
(IMP) $15 = 0x0000000100003c30 (KCObjcBuild`-[CJLPerson cjl_name] at main.m:24)
(lldb) p $6.get(3).name()
(SEL) $16 = "setCjl_name:"
(lldb) p $6.get(3).types()
(const char *) $17 = 0x0000000100003f7a "v24@0:8@16"
(lldb) p $6.get(3).imp(1)
(IMP) $18 = 0x0000000100003c54 (KCObjcBuild`-[CJLPerson setCjl_name:] at main.m:24)
(lldb) p $6.get(4).name()
(SEL) $19 = ".cxx_destruct"
(lldb) p $6.get(5).name()
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
LLDB調(diào)試-類方法classMethod調(diào)試過程
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008200 CJLPerson
(lldb) x/2gx 0x0000000100008200
0x100008200: 0x00000001000081d8 0x0000000100379140
(lldb) po 0x00000001000081d8
CJLPerson
(lldb) p (class_data_bits_t *) 0x00000001000081f8
(class_data_bits_t *) $2 = 0x00000001000081f8
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101029b40
(lldb) p $3->methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100003e28
}
arrayAndFlag = 4294983208
}
}
}
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
ptr = 0x0000000100003e28
}
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100003e28
(lldb) p *$6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
}
(lldb) p $7.get(0).name()
(SEL) $8 = "sayBye"
(lldb) p $7.get(0).types()
(const char *) $9 = 0x0000000100003f5a "v16@0:8"
(lldb) p $7.get(0).imp(1)
(IMP) $10 = 0x0000000100003adc (KCObjcBuild`+[CJLPerson sayBye] at main.m:42)
(lldb) p $7.get(1).name()
(SEL) $11 = "sayLove"
(lldb) p $7.get(1).types()
(const char *) $12 = 0x0000000100003f5a "v16@0:8"
(lldb) p $7.get(1).imp(1)
(IMP) $13 = 0x0000000100003af0 (KCObjcBuild`+[CJLPerson sayLove] at main.m:46)
(lldb) p $7.get(2).name()
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
LLDB調(diào)試-屬性property調(diào)試過程
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008208 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008228
(class_data_bits_t *) $1 = 0x0000000100008228
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001008570f0
(lldb) p $2->properties()
(const property_array_t) $3 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008158
}
arrayAndFlag = 4295000408
}
}
}
(lldb) p $3.list
(const RawPtr<property_list_t>) $4 = {
ptr = 0x0000000100008158
}
(lldb) p $4.ptr
(property_list_t *const) $5 = 0x0000000100008158
(lldb) p *$5
(property_list_t) $6 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $6.get(0).name
(const char *) $7 = 0x0000000100003f1c "cjl_name"
(lldb) p $6.get(0).attributes
(const char *) $8 = 0x0000000100003eb7 "T@\"NSString\",C,N,V_cjl_name"
(lldb) p $6.get(1).name
(const char *) $9 = 0x0000000100003f32 "number"
(lldb) p $6.get(1).attributes
(const char *) $10 = 0x0000000100003ed3 "T@\"NSString\",C,N,V_number"
(lldb) p $6.get(2).name
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
LLDB調(diào)試-成員變量ivar調(diào)試過程
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008200 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008220
(class_data_bits_t *) $1 = 0x0000000100008220
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000105137810
(lldb) p $2->ro()
(const class_ro_t *) $3 = 0x0000000100008080
(lldb) p $3->ivars
(const ivar_list_t *const) $4 = 0x00000001000080c8
(lldb) p *$4
(const ivar_list_t) $5 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
(lldb) p $5.get(0).name
(const char *) $6 = 0x0000000100003ef5 "hobby"
(lldb) p $5.get(0).type
(const char *) $7 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(1).name
(const char *) $8 = 0x0000000100003efb "sex"
(lldb) p $5.get(1).type
(const char *) $9 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(2).name
(const char *) $10 = 0x0000000100003eff "_cjl_name"
(lldb) p $5.get(2).type
(const char *) $11 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(3).name
(const char *) $12 = 0x0000000100003f09 "_number"
(lldb) p $5.get(3).type
(const char *) $13 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(4).name
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
LLDB調(diào)試-協(xié)議protocol調(diào)試過程
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008600 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008620
(class_data_bits_t *) $1 = 0x0000000100008620
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100957f50
(lldb) p $2->protocols()
(const protocol_array_t) $3 = {
list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008410
}
arrayAndFlag = 4295001104
}
}
}
(lldb) p $3.list
(const RawPtr<protocol_list_t>) $4 = {
ptr = 0x0000000100008410
}
(lldb) p $4.ptr
(protocol_list_t *const) $5 = 0x0000000100008410
(lldb) p *$5
(protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)
(lldb) p $6.list[0]
(protocol_ref_t) $7 = 4295001744
(lldb) p (protocol_t *)$7
(protocol_t *) $8 = 0x0000000100008690
(lldb) p *$8
(protocol_t) $9 = {
objc_object = {
isa = {
bits = 4298608840
cls = Protocol
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 537326105
magic = 0
weakly_referenced = 0
unused = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
}
mangledName = 0x0000000100003c4d "CJLPersonDelegate"
protocols = 0x0000000100008310
instanceMethods = 0x0000000100008328
classMethods = 0x0000000100008348
optionalInstanceMethods = 0x0000000100008368
optionalClassMethods = 0x0000000100008388
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x00000001000083a8
_demangledName = 0x0000000000000000
_classProperties = nil
}
(lldb) p/x 4298608840
(long) $10 = 0x00000001003790c8
(lldb) p/x 0x00000001003790c8 & 0x0000000ffffffff8ULL
(unsigned long long) $11 = 0x00000001003790c8
(lldb) po 0x00000001003790c8
Protocol
(lldb) p $9.instanceMethods
(method_list_t *) $13 = 0x0000000100008328
(lldb) p *$13
(method_list_t) $14 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $14.get(0).name()
(SEL) $15 = "sendDataToController:"
(lldb) p $14.get(0).types()
(const char *) $16 = 0x0000000100003d50 "v24@0:8@16"
(lldb) p $14.get(0).imp(1)
(IMP) $17 = 0x0000000000000000
(lldb) p $9.classMethods
(method_list_t *) $18 = 0x0000000100008348
(lldb) p *$18
(method_list_t) $19 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $19.get(0).name()
(SEL) $20 = "sendString:"
(lldb) p $19.get(0).types()
(const char *) $21 = 0x0000000100003d50 "v24@0:8@16"
(lldb) p $19.get(0).imp(1)
(IMP) $22 = 0x0000000000000000
(lldb) p $9.optionalInstanceMethods
(method_list_t *) $23 = 0x0000000100008368
(lldb) p *$23
(method_list_t) $24 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $24.get(0).name()
(SEL) $25 = "showKeyboard"
(lldb) p $24.get(0).types()
(const char *) $26 = 0x0000000100003d48 "v16@0:8"
(lldb) p $24.get(0).imp(1)
(IMP) $27 = 0x0000000000000000
(lldb) p $9.optionalClassMethods
(method_list_t *) $28 = 0x0000000100008388
(lldb) p *$28
(method_list_t) $29 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $29.get(0).name()
(SEL) $30 = "closeKeyboard"
(lldb) p $29.get(0).types()
(const char *) $31 = 0x0000000100003d48 "v16@0:8"
(lldb) p $29.get(0).imp(1)
(IMP) $32 = 0x0000000000000000
(lldb)
bits存儲類信息的共同點
無論方法、屬性、協(xié)議、成員變量
都放在了對應(yīng)結(jié)構(gòu)體的數(shù)組里,method_list_t
、property_list_t
、ivar_list_t
、protocol_list_t
,說明這些是有序
而且是值為真
LLDB
調(diào)試獲取xxxx_list_t
里面的元素方式是p $5.get(0)
p $5.get(1)
...
p $5.get(N)
,get(index)
下標取法(圓括號+下標
)
那么接下來objc_class
里面非常重要的cache
是不是也是這樣的存儲方式呢?
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
}
(property_list_t) $6 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(const ivar_list_t) $5 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
(protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)
3.isa、superclass、bits總結(jié)
** objc_class、objc_objcet、Class**
1.Class
是objc_class
是類
,類
來自于底層objc_class
結(jié)構(gòu)體
2.id
是objc_object
是對象
,即對象
來自于objc_object
結(jié)構(gòu)體
3.objc_class : objc_object
,objc_class
繼承自objc_object
結(jié)構(gòu)體,objc_class
的isa
繼承自objc_object
,所以,類
和對象都有Class isa
指針
4.類
有superclass
有繼承關(guān)系,對象
沒有superclass
沒有繼承關(guān)系
元類
1.我們都知道 對象
的isa
是指向類
,類
其實也是一個對象
,可以稱為類對象
,其isa
指向蘋果定義的元類
2.元類
是系統(tǒng)
給的,其定義
和創(chuàng)建
都是由編譯器
完成,在這個過程中,類
的歸屬來自于元類
3.元類
是類對象 的類,每個類
都有一個獨一無二的元類
用來存儲 類方法
的相關(guān)信息。
元類
本身是沒有名稱
的,由于與類
相關(guān)聯(lián),所以使用了同類
名一樣的名稱,但是本質(zhì)和底層編譯的名稱是不一樣的
4.CJLPerson
不是CJLPerson_metaClass
(CJLPerson元類),內(nèi)存地址
不同,但是每一個只有一份
5.NSobject
不是NSobject_metaClass
(根元類),內(nèi)存地址
不同,但是每一個只有一份
** isa**
對象
---> 類
---> 元類
---> NSobject(根元類)
---> NSobject(根元類)
superclass
1.只有類有繼承關(guān)系,對象沒有繼承關(guān)系
子類
---> 父類
---> ...
---> NSObject
---> nil
子元類
--->父元類
---> ...
---> NSObject(根元類)
---> NSObject
---> nil
bits
1.實例方法
在類
里面,類方法
在元類
里面,類方法
相當(dāng)于元類
的實例方法
,由于只有實例方法
和類方法
,所以,根元類
里面是沒有方法列表
的
2.成員變量
列表里面有屬性
,屬性
列表里面沒有成員變量
3.實例方法列表
包含屬性
的setter
和getter
方法
4.實例對象
來自于objc_object
結(jié)構(gòu)體
5.實例方法
、類方法
、協(xié)議
、屬性
、成員變量
都存在對應(yīng)類
或者元類
的objc_class
--->class_data_bits_t bits
成員變量里面
6.協(xié)議
是一個對象
繼承自objc_object
結(jié)構(gòu)體,協(xié)議
里面通過各種方法列表
存儲協(xié)議方法
問題
1.Class
到底是什么?Class
與類
之間的關(guān)系?
Class
就是類
,也是類的首地址-指針
,存儲內(nèi)容是類名
,類似于數(shù)組名和數(shù)組首地址
2.對象
的isa
和類
的isa
之間的區(qū)別?分別存儲的是什么值?
類的isa
64位
中只在對應(yīng)的33位
存儲類的首地址
,對象的isa 64位
中除了類信息還有其他信息,nonpointer = 0
的話,對象isa
只存儲類信息,但是多數(shù)對象是nonpointer = 1的
3.對象方法
存儲在類
中,那類方法
存儲在哪里呢?如果一直有相應(yīng)的類
來存儲那么盡頭在哪里呢?
對象方法
存儲在類
中,類方法
存儲在元類
中,由于只有類方法
和類方法
,所以,根元類里面是沒有方法列表的
4.isa
與superclass
是什么關(guān)系? isa
到底有哪些作用?
isa
與superclass
都是Class
類型的64位
指針,都是存儲類的指針
,沒有直接的關(guān)系,isa
偏向于數(shù)據(jù)的操作邏輯屬于誰,即方法
存儲在哪里,找到后去操作數(shù)據(jù),superclass
偏向于父類
在哪里,即更高級的操作邏輯屬于誰,superclass
是幫助isa
在不同層級的父類中查找操作邏輯
5.類
在內(nèi)存中到底存在幾份
?
類
在內(nèi)存中只有一份,類
和元類
雖然看著名字一樣,但是本質(zhì)是兩個獨立的類,內(nèi)存空間
、底層編譯
都是不同的