Clang
是一個C++
編寫、基於LLVM
、發佈於LLVM
BSD
許可證下的C/C++/Objective-C/ Objective-C++
編譯器。ios
使用Clang
將main.m
文件編譯成C++
文件c++
clang -rewrite-objc main.m -o main.cpp
把目標文件編譯成c++文件markdown
遇到UIKit報錯問題:架構
解決clang編譯遇到UIKit報錯的兩種方式:app
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-
14.3.0
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.3
.sdk main.miphone
路徑中的系統版本按照xCode中SDK版本修改(當前爲模擬器SDK)ide
xcrun
命令xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp函數
使用clang -rewrite-objc main.m -o main.cpp
命令編譯main.m
文件,main.m
文件內容以下:post
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface ABPerson : NSObject
@property (nonatomic,strong) NSString *name;
@end
@implementation ABPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
複製代碼
main.cpp
文件中的ABPerson
以下:優化
struct ABPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
複製代碼
struct NSObject_IMPL {
Class isa;
};
複製代碼
//Class就是結構體指針
typedef struct objc_class *Class;
複製代碼
因此,對象在底層的本質就是結構體
//getter方法
static NSString * _I_ABPerson_name(ABPerson * self, SEL _cmd)
{
return (*(NSString **)((char *)self + OBJC_IVAR_$_ABPerson$_name));
}
//setter方法
static void _I_ABPerson_setName_(ABPerson * self, SEL _cmd, NSString *name)
{
(*(NSString **)((char *)self + OBJC_IVAR_$_ABPerson$_name)) = name;
}
複製代碼
成員變量的內存訪問是經過先拿到對象的首地址加上變量的偏移地址,再根據計算的地址訪問內存:
(char *)self
:對象的首地址OBJC_IVAR_$_ABPerson$_name
:成員變量name的偏移地址*(NSString **)((char *)self + OBJC_IVAR_$_ABPerson$_name)
成員變量name的內存struct StructCar1 {
BOOL front;
BOOL back;
BOOL left;
BOOL right;
};
struct StructCar2 {
BOOL front: 1;
BOOL back: 1;
BOOL left: 1;
BOOL right: 1;
};
複製代碼
StructCar1
佔4
個字節,由於其內部成員變量都是BOOL
類型,其實用1
個字節就能放下StructCar2
佔1
個字節,節省了3個字節。結構體(struct)中全部變量是「共存」的,優勢是「有容乃大」,全面;缺點是結構體內存空間分配是粗放的,無論用不用全分配。
共用體(union)中是各變量是「互斥」的,缺點是不夠「包容」;優勢是內存使用更爲精細靈活,也節省了內存空間
ISA
分爲純的ISA
和NONPOINTER_ISA
,NONPOINTER_ISA
除了包含一個純指針外,還包含了類的一些信息。
在探索alloc流程一文中,最終會經過initIsa
將從堆申請的結構體指針和當前的class
綁定在一塊兒。
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
複製代碼
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
//省略部分代碼
}
複製代碼
//共用體
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);
};
複製代碼
ISA_BITFIELD
在arm64
及x86_64
架構下的定義
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
//模擬器
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
//真機
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
//macOS
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
複製代碼
nonpointer
:表示是否對 isa 指針開啓指針優化,0:純isa指針,1:不止是類對象地址,isa 中包含了類信息、對象的引用計數等has_assoc
:關聯對象標誌位,0沒有,1存在has_cxx_dtor
:該對象是否有 C++ 或者 Objc 的析構器,若是有析構函數,則須要作析構邏輯, 若是沒有,則能夠更快的釋放對象shiftcls
:存儲類指針的值。開啓指針優化的狀況下,在 arm64 架構中有 33 位用來存儲類指針-magic
:用於調試器判斷當前對象是真的對象仍是沒有初始化的空間
weakly_referenced
:志對象是否被指向或者曾經指向一個 ARC 的弱變量,沒有弱引用的對象能夠更快釋放
unused
:是否未被使用has_sidetable_rc
:散列表,當對象引用技術大於 10 時,則須要借用該變量存儲進位extra_rc
:當表示該對象的引用計數值,其實是引用計數值減 1, 例如,若是對象的引用計數爲 10,那麼 extra_rc 爲 9。若是引用計數大於 10, 則須要使用到下面的 has_sidetable_rcISA_BITFIELD
各元素存儲分佈圖
經過ISA
的位運算拿到shiftcls
。以macOS
爲例,shiftcls
在64
位結構中的位置:右邊有3
位,左邊有17
位。本身包含44
位。
代碼lldb驗證
倆紅框值是相等的,說明位運算成功拿到了
shiftcls
。
ISA
是經過共用體(聯合體)互斥的特性,肯定ISA
是純的ISA
仍是NONPOINTER_ISA
,並經過位域來實現節約空間。