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
,并经过位域来实现节约空间。