1、對(duì)象的本質(zhì)及其擴(kuò)展
1.1什么是clang?
在探索對(duì)象本質(zhì)之前,我們先來(lái)學(xué)習(xí)一下:
Clang是?個(gè)C語(yǔ)?、C++、Objective-C語(yǔ)?的輕量級(jí)編譯器。源代碼發(fā)布于BSD協(xié)議下。Clang將?持其普通lambda表達(dá)式、返回類型的簡(jiǎn)化處理以及更好的處理constexpr關(guān)鍵字。簡(jiǎn)單理解Clang是?個(gè)由Apple主導(dǎo)編寫(xiě),基于LLVM的C/C++/Objective-C編譯器。
1.2 clang常使用技巧?
1.clang -rewrite-objc main.m -o main.cpp 把?標(biāo)?件編譯成c++?件
舉個(gè)??:
-打開(kāi)項(xiàng)目工程




此時(shí),我們就獲取到了main.m 目錄下就獲取到對(duì)應(yīng)main.cpp源文件
接下來(lái)有個(gè)東西要注意的是,把?標(biāo)?件編譯成c++?件可能會(huì)碰到UIKit報(bào)錯(cuò)問(wèn)題,可以用如下命令進(jìn)行解決,其中需要更改相應(yīng)的版本號(hào):
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
因?yàn)?strong>xcode安裝的時(shí)候順帶安裝了xcrun命令,xcrun命令在clang的基礎(chǔ)上進(jìn)?了?些封裝,要更好??些,可以用如下方法進(jìn)行編譯:
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp(模擬器)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp(?機(jī))
1.3 通過(guò)main.cpp源文件的源碼我們可以進(jìn)一步分析,對(duì)象的本質(zhì)
1.3.1首先,先看先main.m的代碼
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// 對(duì)象在底層的本質(zhì)就是結(jié)構(gòu)體
@interface LGPerson : NSObject
@property (nonatomic, strong) NSString *KCName;
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
1.3.2.在大打開(kāi)看先main.cpp的代碼,一個(gè)main的源碼有十幾萬(wàn)行,一下子暈頭轉(zhuǎn)向,這怎么看:

全局搜索下LGPerson:
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_KCName;
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_KCName;
};
// @property (nonatomic, strong) NSString *KCName;
/* @end */
// @implementation LGPerson
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
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7l_wm83md7d5w3f9csj6_7_tlrh0000gn_T_main_07314a_mi_0);
}
return 0;
}
繼續(xù)探索看看:typedef struct objc_object LGPerson; 得到 objc_object源碼,是一個(gè)結(jié)構(gòu)體
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_KCName;
};
LGPerson_IMPL 包含了我們定義的成員變量_KCName, 還有一個(gè)結(jié)構(gòu)體struct NSObject_IMPL NSObject_IVARS;
繼續(xù)往下查看NSObject_IMPL
struct NSObject_IMPL {
Class isa;
};
在看看:Class
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa __attribute__((deprecated));
} __attribute__((unavailable));
總結(jié):Class 的本質(zhì) 是一個(gè)objc_class 類型,意味著當(dāng)前的Class是一個(gè)結(jié)構(gòu)體指針, Class此時(shí)充當(dāng)只是一個(gè)別名而已,一步步查看源碼下來(lái):對(duì)象在底層的本質(zhì)就是結(jié)構(gòu)體 。
同時(shí):平時(shí)我們定義id Person 為什么沒(méi)有帶* 號(hào),通過(guò)底層代碼可以發(fā)現(xiàn) id 的本質(zhì) 是一個(gè)objc_object* 類型,意味著當(dāng)前的id是一個(gè)結(jié)構(gòu)體指針, id此時(shí)充當(dāng)只是一個(gè)別名而已。
typedef struct objc_object *id;
1.4 get 與set 方法的擴(kuò)展分析:
以_KCName為例:
//當(dāng)前的隱藏參數(shù)
static NSString * _I_LGPerson_KCName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_KCName)); }
//set方法
//存值的地方
static void _I_LGPerson_setKCName_(LGPerson * self, SEL _cmd, NSString *KCName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_KCName)) = KCName; }
所有的對(duì)象都在當(dāng)前的堆區(qū),開(kāi)辟內(nèi)存空間,那么內(nèi)存空間里面裝什么呢?裝成員變量,第一個(gè)成員變量裝的是什么,是isa?如何獲取,可以通過(guò)控制臺(tái)x/4g 去獲取
所有的內(nèi)存,首先是怎么訪問(wèn)的?
首先第一點(diǎn)拿到內(nèi)存的地址,以LGPerson為例,首先要拿到LGPerson的首地址,之后在進(jìn)行OBJC_IVAR量的平移,拿到KCName對(duì)應(yīng)地址,從而獲取地址里面KCName的值,所以說(shuō)KCName的值不是天然存在的。
這就是為什么說(shuō)get方法 return (*(NSString **)((char *)self + OBJC_IVAR__KCName))的原因。
2、nonPointerIsa的分析:
文章未完成,接下來(lái)繼續(xù)分析nonPointerIsa