isa
- 在
arm64
之前,isa
就是一個普通的指針,它指向class
ormeta-class
- 在
arm64
之后,對isa
進行了優(yōu)化,變成了一個共同體(union
)結構,還使用位域來存儲更多信息.
先來看一個例子:新建person
類
@interface Person : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandSome:(BOOL)handSome;
- (BOOL)getTall;
- (BOOL)getRich;
- (BOOL)getHandSome;
@end
再來.m文件
//掩碼 進行位運算
//#define ISTallMASK 1
//#define ISRichMASK 2
//#define ISHandSomeMASK 4
#define ISTallMASK (1<<0)
#define ISRichMASK (1<<1)
#define ISHandSomeMASK (1<<2)
@interface Person()
{
char _tallRichHandsome;
}
@end
@implementation Person
- (instancetype)init
{
self = [super init];
if (self) {
//從右至左tall rich handsome
_tallRichHandsome = 0b00000000;
}
return self;
}
- (void)setTall:(BOOL)tall{
if (tall) {
// | 或運算 有1則為1
/*
0000 0001
0000 0001
*/
_tallRichHandsome = _tallRichHandsome | ISTallMASK;
}else{
/*
0000 0101
1111 1110
先取反 在進行&運算 保證其他為不變 標志位必為0
*/
_tallRichHandsome = _tallRichHandsome & ~ISTallMASK;
}
}
- (void)setRich:(BOOL)rich{
if (rich) {
_tallRichHandsome = _tallRichHandsome | ISRichMASK;
}else{
_tallRichHandsome = _tallRichHandsome & ~ISRichMASK;
}
}
- (void)setHandSome:(BOOL)handSome{
if (handSome) {
_tallRichHandsome = _tallRichHandsome | ISHandSomeMASK;
}else{
_tallRichHandsome = _tallRichHandsome & ~ISHandSomeMASK;
}
}
- (BOOL)getTall{
//&運算 同為1則為1
/*
0000 0000
0000 0001 // 十進制1*2^0 = 1
*/
return !!(_tallRichHandsome & ISTallMASK);
}
- (BOOL)getRich{
/*
0000 0000
0000 0010 // 十進制1*2^1 = 2
*/
return !!(_tallRichHandsome & ISRichMASK);
}
- (BOOL)getHandSome{
/*
0000 0000
0000 0100 // 十進制1*2^2 = 4
*/
return !!(_tallRichHandsome & ISHandSomeMASK);
}
這樣我們就實現(xiàn)了一個字節(jié)存儲3個BOOL變量.如果以后要增加到4個BOOL變量,不免有些麻煩,接下來優(yōu)化一下.
@interface Person()
{
// char _tallRichHandsome;
//位域
struct {
char tall :1;//表示只占一位
char rich :1;
char handsome :1;
}_tallRichHandsome;
}
@implementation Person
- (void)setTall:(BOOL)tall{
_tallRichHandsome.tall = tall;
}
- (void)setRich:(BOOL)rich{
_tallRichHandsome.rich = rich;
}
- (void)setHandSome:(BOOL)handSome{
_tallRichHandsome.handsome = handSome;
}
- (BOOL)getTall{
// BOOL isRet = _tallRichHandsome.tall
//這里強制轉換 一個BOOL 類型是占一個字節(jié) tall這里是0x01 強轉后變 1111 1111 = 255,所以這里需要2次取反拿到正確的值.
return !!(_tallRichHandsome.tall);
}
- (BOOL)getRich{
return !!(_tallRichHandsome.rich);
}
- (BOOL)getHandSome{
return !!(_tallRichHandsome.handsome);
@end
Person *persn = [[Person alloc]init];
[persn setTall:YES];
[persn setRich:NO];
[persn setHandSome:NO];
NSLog(@"%d-- %d ---%d",persn.getRich,persn.getTall,persn.getHandSome);
(lldb) p/x &(persn->_tallRichHandsome)
((anonymous struct) *) $0 = 0x000000010281f2a8
(lldb) x 0x000000010281f2a8
0x10281f2a8: 01 00 00 00 00 00 00 00 63 46 1b 41 ff 7f 00 00 ........cF.A....
0x10281f2b8: 4a 97 fb 42 ff 7f 00 00 b9 be 52 45 ff 7f 00 00 J..B......RE....
看01 00 00 00 00 00 00 00
第2個十六進制位,轉換成二進制是0000 0001
說明tall
被放在最右邊.
接下來看看蘋果的做法
@interface Person()
{
// 位域
union{
char bits;
struct {
char tall :1;
char rich :1;
char handsome :1;
};
}_tallRichHandsome;
}
@end
union
是一個共同體,bits
占一個字節(jié),struct
也占一個字節(jié).
.m文件
- (void)setTall:(BOOL)tall{
if (tall) {
_tallRichHandsome.bits = _tallRichHandsome.bits | ISTallMASK;
}else{
_tallRichHandsome.bits = _tallRichHandsome.bits & ~ISTallMASK;
}
}
再來看isa
的結構
union isa_t
{
Class cls;
uintptr_t bits;
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
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 deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
}
- nonpointer
0忧便,代表普通的指針,存儲著Class帽借、Meta-Class對象的內存地址
1茬腿,代表優(yōu)化過,使用位域存儲更多的信息 - has_assoc
是否有設置過關聯(lián)對象宜雀,如果沒有切平,釋放時會更快 - has_cxx_dtor
是否有C++的析構函數(shù)(.cxx_destruct),如果沒有辐董,釋放時會更快 - shiftcls
存儲著Class悴品、Meta-Class對象的內存地址信息 - magic
用于在調試時分辨對象是否未完成初始化 - weakly_referenced
是否有被弱引用指向過,如果沒有简烘,釋放時會更快 - deallocating
對象是否正在釋放 - extra_rc
里面存儲的值是引用計數(shù)器減1 - has_sidetable_rc
引用計數(shù)器是否過大無法存儲在isa中
如果為1苔严,那么引用計數(shù)會存儲在一個叫SideTable的類的屬性中
Class結構
struct objc_class {
Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
bits & FAST_DATA_MASK 得到
//類的初始信息
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;//方法列表
property_array_t properties;//屬性列表
protocol_array_t protocols;//協(xié)議列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
{
method_array_t
是一個二維數(shù)組里面最終存放的是method_t
struct method_t {
SEL name;
const char *types;
IMP imp;
}
- name; 函數(shù)名
- types; 編碼(函數(shù)返回值類型,參數(shù)類型)
- imp; 函數(shù)地址(指向函數(shù)的指針)
SEL
代表方法\函數(shù)名,一般叫做選擇器孤澎,底層結構跟char *
類似
可以通過@selector()
和sel_registerName()
獲得
可以通過sel_getName()
和NSStringFromSelector()
轉成字符串
不同類中相同名字的方法届氢,所對應的方法選擇器是相同的
iOS中提供了一個叫做@encode的指令,可以將具體的類型表示成字符串編碼
image.png
image.png
接下來看看cache_t
結構
struct bucket_t {
cache_key_t _key;// SEL作為key
IMP _imp;//函數(shù)的內存地址
};
struct cache_t {
struct bucket_t *_buckets;//哈希表
mask_t _mask;//哈希表長度 - 1
mask_t _occupied;//已經(jīng)緩存的方法數(shù)量
}